github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapstate/snapstate_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2018 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 "bytes" 24 "context" 25 "encoding/json" 26 "errors" 27 "fmt" 28 "io/ioutil" 29 "os" 30 "path/filepath" 31 "sort" 32 "strings" 33 "testing" 34 "time" 35 36 . "gopkg.in/check.v1" 37 "gopkg.in/tomb.v2" 38 39 "github.com/snapcore/snapd/asserts" 40 "github.com/snapcore/snapd/bootloader" 41 "github.com/snapcore/snapd/bootloader/bootloadertest" 42 "github.com/snapcore/snapd/dirs" 43 "github.com/snapcore/snapd/gadget" 44 "github.com/snapcore/snapd/interfaces" 45 "github.com/snapcore/snapd/logger" 46 "github.com/snapcore/snapd/overlord" 47 "github.com/snapcore/snapd/overlord/auth" 48 "github.com/snapcore/snapd/overlord/configstate/config" 49 "github.com/snapcore/snapd/overlord/hookstate" 50 "github.com/snapcore/snapd/overlord/ifacestate/ifacerepo" 51 "github.com/snapcore/snapd/overlord/snapstate" 52 "github.com/snapcore/snapd/overlord/snapstate/backend" 53 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 54 "github.com/snapcore/snapd/overlord/state" 55 "github.com/snapcore/snapd/release" 56 "github.com/snapcore/snapd/snap" 57 "github.com/snapcore/snapd/snap/snaptest" 58 "github.com/snapcore/snapd/store" 59 "github.com/snapcore/snapd/testutil" 60 "github.com/snapcore/snapd/timeutil" 61 62 // So it registers Configure. 63 _ "github.com/snapcore/snapd/overlord/configstate" 64 ) 65 66 func TestSnapManager(t *testing.T) { TestingT(t) } 67 68 type snapmgrTestSuite struct { 69 testutil.BaseTest 70 o *overlord.Overlord 71 state *state.State 72 se *overlord.StateEngine 73 snapmgr *snapstate.SnapManager 74 75 fakeBackend *fakeSnappyBackend 76 fakeStore *fakeStore 77 78 user *auth.UserState 79 user2 *auth.UserState 80 user3 *auth.UserState 81 } 82 83 func (s *snapmgrTestSuite) settle(c *C) { 84 err := s.o.Settle(5 * time.Second) 85 c.Assert(err, IsNil) 86 } 87 88 var _ = Suite(&snapmgrTestSuite{}) 89 90 var fakeRevDateEpoch = time.Date(2018, 1, 0, 0, 0, 0, 0, time.UTC) 91 92 func (s *snapmgrTestSuite) SetUpTest(c *C) { 93 s.BaseTest.SetUpTest(c) 94 dirs.SetRootDir(c.MkDir()) 95 96 s.o = overlord.Mock() 97 s.state = s.o.State() 98 99 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 100 101 s.fakeBackend = &fakeSnappyBackend{} 102 s.fakeBackend.emptyContainer = emptyContainer(c) 103 s.fakeStore = &fakeStore{ 104 fakeCurrentProgress: 75, 105 fakeTotalProgress: 100, 106 fakeBackend: s.fakeBackend, 107 state: s.state, 108 } 109 110 oldSetupInstallHook := snapstate.SetupInstallHook 111 oldSetupPreRefreshHook := snapstate.SetupPreRefreshHook 112 oldSetupPostRefreshHook := snapstate.SetupPostRefreshHook 113 oldSetupRemoveHook := snapstate.SetupRemoveHook 114 snapstate.SetupInstallHook = hookstate.SetupInstallHook 115 snapstate.SetupPreRefreshHook = hookstate.SetupPreRefreshHook 116 snapstate.SetupPostRefreshHook = hookstate.SetupPostRefreshHook 117 snapstate.SetupRemoveHook = hookstate.SetupRemoveHook 118 119 var err error 120 s.snapmgr, err = snapstate.Manager(s.state, s.o.TaskRunner()) 121 c.Assert(err, IsNil) 122 123 AddForeignTaskHandlers(s.o.TaskRunner(), s.fakeBackend) 124 125 snapstate.SetSnapManagerBackend(s.snapmgr, s.fakeBackend) 126 127 s.o.AddManager(s.snapmgr) 128 s.o.AddManager(s.o.TaskRunner()) 129 s.se = s.o.StateEngine() 130 c.Assert(s.o.StartUp(), IsNil) 131 132 s.BaseTest.AddCleanup(snapstate.MockSnapReadInfo(s.fakeBackend.ReadInfo)) 133 s.BaseTest.AddCleanup(snapstate.MockOpenSnapFile(s.fakeBackend.OpenSnapFile)) 134 revDate := func(info *snap.Info) time.Time { 135 if info.Revision.Local() { 136 panic("no local revision should reach revisionDate") 137 } 138 // for convenience a date derived from the revision 139 return fakeRevDateEpoch.AddDate(0, 0, info.Revision.N) 140 } 141 s.BaseTest.AddCleanup(snapstate.MockRevisionDate(revDate)) 142 143 s.BaseTest.AddCleanup(func() { 144 snapstate.SetupInstallHook = oldSetupInstallHook 145 snapstate.SetupPreRefreshHook = oldSetupPreRefreshHook 146 snapstate.SetupPostRefreshHook = oldSetupPostRefreshHook 147 snapstate.SetupRemoveHook = oldSetupRemoveHook 148 149 dirs.SetRootDir("/") 150 }) 151 152 s.BaseTest.AddCleanup(snapstate.MockReRefreshRetryTimeout(time.Second / 200)) 153 s.BaseTest.AddCleanup(snapstate.MockReRefreshUpdateMany(func(context.Context, *state.State, []string, int, snapstate.UpdateFilter, *snapstate.Flags, string) ([]string, []*state.TaskSet, error) { 154 return nil, nil, nil 155 })) 156 157 oldAutomaticSnapshot := snapstate.AutomaticSnapshot 158 snapstate.AutomaticSnapshot = func(st *state.State, instanceName string) (ts *state.TaskSet, err error) { 159 task := st.NewTask("save-snapshot", "...") 160 ts = state.NewTaskSet(task) 161 return ts, nil 162 } 163 164 oldAutomaticSnapshotExpiration := snapstate.AutomaticSnapshotExpiration 165 snapstate.AutomaticSnapshotExpiration = func(st *state.State) (time.Duration, error) { return 1, nil } 166 s.BaseTest.AddCleanup(func() { 167 snapstate.AutomaticSnapshot = oldAutomaticSnapshot 168 snapstate.AutomaticSnapshotExpiration = oldAutomaticSnapshotExpiration 169 }) 170 171 s.state.Lock() 172 snapstate.ReplaceStore(s.state, s.fakeStore) 173 s.user, err = auth.NewUser(s.state, "username", "email@test.com", "macaroon", []string{"discharge"}) 174 c.Assert(err, IsNil) 175 s.user2, err = auth.NewUser(s.state, "username2", "email2@test.com", "macaroon2", []string{"discharge2"}) 176 c.Assert(err, IsNil) 177 // 3 has no store auth 178 s.user3, err = auth.NewUser(s.state, "username3", "email2@test.com", "", nil) 179 c.Assert(err, IsNil) 180 181 s.state.Set("seeded", true) 182 s.state.Set("seed-time", time.Now()) 183 184 r := snapstatetest.MockDeviceModel(DefaultModel()) 185 s.BaseTest.AddCleanup(r) 186 187 s.state.Set("refresh-privacy-key", "privacy-key") 188 snapstate.Set(s.state, "core", &snapstate.SnapState{ 189 Active: true, 190 Sequence: []*snap.SideInfo{ 191 {RealName: "core", Revision: snap.R(1)}, 192 }, 193 Current: snap.R(1), 194 SnapType: "os", 195 }) 196 s.state.Unlock() 197 198 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 199 return nil, nil 200 } 201 } 202 203 func (s *snapmgrTestSuite) TearDownTest(c *C) { 204 s.BaseTest.TearDownTest(c) 205 snapstate.ValidateRefreshes = nil 206 snapstate.AutoAliases = nil 207 snapstate.CanAutoRefresh = nil 208 } 209 210 type ForeignTaskTracker interface { 211 ForeignTask(kind string, status state.Status, snapsup *snapstate.SnapSetup) 212 } 213 214 func AddForeignTaskHandlers(runner *state.TaskRunner, tracker ForeignTaskTracker) { 215 // Add fake handlers for tasks handled by interfaces manager 216 fakeHandler := func(task *state.Task, _ *tomb.Tomb) error { 217 task.State().Lock() 218 kind := task.Kind() 219 status := task.Status() 220 snapsup, err := snapstate.TaskSnapSetup(task) 221 task.State().Unlock() 222 if err != nil { 223 return err 224 } 225 226 tracker.ForeignTask(kind, status, snapsup) 227 228 return nil 229 } 230 runner.AddHandler("setup-profiles", fakeHandler, fakeHandler) 231 runner.AddHandler("auto-connect", fakeHandler, nil) 232 runner.AddHandler("auto-disconnect", fakeHandler, nil) 233 runner.AddHandler("remove-profiles", fakeHandler, fakeHandler) 234 runner.AddHandler("discard-conns", fakeHandler, fakeHandler) 235 runner.AddHandler("validate-snap", fakeHandler, nil) 236 runner.AddHandler("transition-ubuntu-core", fakeHandler, nil) 237 runner.AddHandler("transition-to-snapd-snap", fakeHandler, nil) 238 239 // Add handler to test full aborting of changes 240 erroringHandler := func(task *state.Task, _ *tomb.Tomb) error { 241 return errors.New("error out") 242 } 243 runner.AddHandler("error-trigger", erroringHandler, nil) 244 245 runner.AddHandler("save-snapshot", func(task *state.Task, _ *tomb.Tomb) error { 246 return nil 247 }, nil) 248 runner.AddHandler("run-hook", func(task *state.Task, _ *tomb.Tomb) error { 249 return nil 250 }, nil) 251 runner.AddHandler("configure-snapd", func(t *state.Task, _ *tomb.Tomb) error { 252 return nil 253 }, nil) 254 255 } 256 257 func (s *snapmgrTestSuite) TestCleanSnapStateGet(c *C) { 258 snapst := snapstate.SnapState{ 259 Sequence: []*snap.SideInfo{ 260 {RealName: "foo", Revision: snap.R(1)}, 261 }, 262 Current: snap.R(1), 263 SnapType: "os", 264 Channel: "foo", 265 InstanceKey: "bar", 266 } 267 268 s.state.Lock() 269 270 defer s.state.Unlock() 271 snapstate.Set(s.state, "no-instance-key", &snapstate.SnapState{ 272 Sequence: []*snap.SideInfo{ 273 {RealName: "core", Revision: snap.R(1)}, 274 }, 275 Current: snap.R(1), 276 SnapType: "app", 277 }) 278 279 err := snapstate.Get(s.state, "bar", nil) 280 c.Assert(err, ErrorMatches, "internal error: snapst is nil") 281 282 err = snapstate.Get(s.state, "no-instance-key", &snapst) 283 c.Assert(err, IsNil) 284 c.Assert(snapst, DeepEquals, snapstate.SnapState{ 285 Sequence: []*snap.SideInfo{ 286 {RealName: "core", Revision: snap.R(1)}, 287 }, 288 Current: snap.R(1), 289 SnapType: "app", 290 }) 291 } 292 293 func (s *snapmgrTestSuite) TestStore(c *C) { 294 s.state.Lock() 295 defer s.state.Unlock() 296 297 sto := &store.Store{} 298 snapstate.ReplaceStore(s.state, sto) 299 store1 := snapstate.Store(s.state, nil) 300 c.Check(store1, Equals, sto) 301 302 // cached 303 store2 := snapstate.Store(s.state, nil) 304 c.Check(store2, Equals, sto) 305 } 306 307 func (s *snapmgrTestSuite) TestStoreWithDeviceContext(c *C) { 308 s.state.Lock() 309 defer s.state.Unlock() 310 311 stoA := &store.Store{} 312 snapstate.ReplaceStore(s.state, stoA) 313 store1 := snapstate.Store(s.state, nil) 314 c.Check(store1, Equals, stoA) 315 316 stoB := &store.Store{} 317 318 // cached 319 store2 := snapstate.Store(s.state, &snapstatetest.TrivialDeviceContext{}) 320 c.Check(store2, Equals, stoA) 321 322 // from context 323 store3 := snapstate.Store(s.state, &snapstatetest.TrivialDeviceContext{CtxStore: stoB}) 324 c.Check(store3, Equals, stoB) 325 } 326 327 func (s *snapmgrTestSuite) TestUserFromUserID(c *C) { 328 s.state.Lock() 329 defer s.state.Unlock() 330 331 tests := []struct { 332 ids []int 333 u *auth.UserState 334 invalid bool 335 }{ 336 {[]int{0}, nil, false}, 337 {[]int{2}, s.user2, false}, 338 {[]int{99}, nil, true}, 339 {[]int{1, 99}, s.user, false}, 340 {[]int{99, 0}, nil, false}, 341 {[]int{99, 2}, s.user2, false}, 342 {[]int{99, 100}, nil, true}, 343 } 344 345 for _, t := range tests { 346 u, err := snapstate.UserFromUserID(s.state, t.ids...) 347 c.Check(u, DeepEquals, t.u) 348 if t.invalid { 349 c.Check(err, Equals, auth.ErrInvalidUser) 350 } else { 351 c.Check(err, IsNil) 352 } 353 } 354 } 355 356 const ( 357 unlinkBefore = 1 << iota 358 cleanupAfter 359 maybeCore 360 runCoreConfigure 361 doesReRefresh 362 updatesGadget 363 noConfigure 364 ) 365 366 func taskKinds(tasks []*state.Task) []string { 367 kinds := make([]string, len(tasks)) 368 for i, task := range tasks { 369 k := task.Kind() 370 if k == "run-hook" { 371 var hooksup hookstate.HookSetup 372 if err := task.Get("hook-setup", &hooksup); err != nil { 373 panic(err) 374 } 375 k = fmt.Sprintf("%s[%s]", k, hooksup.Hook) 376 } 377 kinds[i] = k 378 } 379 return kinds 380 } 381 382 func verifyInstallTasks(c *C, opts, discards int, ts *state.TaskSet, st *state.State) { 383 kinds := taskKinds(ts.Tasks()) 384 385 expected := []string{ 386 "prerequisites", 387 "download-snap", 388 "validate-snap", 389 "mount-snap", 390 } 391 if opts&unlinkBefore != 0 { 392 expected = append(expected, 393 "stop-snap-services", 394 "remove-aliases", 395 "unlink-current-snap", 396 ) 397 } 398 if opts&updatesGadget != 0 { 399 expected = append(expected, "update-gadget-assets") 400 } 401 expected = append(expected, 402 "copy-snap-data", 403 "setup-profiles", 404 "link-snap", 405 ) 406 expected = append(expected, 407 "auto-connect", 408 "set-auto-aliases", 409 "setup-aliases", 410 "run-hook[install]", 411 "start-snap-services") 412 for i := 0; i < discards; i++ { 413 expected = append(expected, 414 "clear-snap", 415 "discard-snap", 416 ) 417 } 418 if opts&cleanupAfter != 0 { 419 expected = append(expected, 420 "cleanup", 421 ) 422 } 423 if opts&noConfigure == 0 { 424 expected = append(expected, 425 "run-hook[configure]", 426 ) 427 } 428 expected = append(expected, 429 "run-hook[check-health]", 430 ) 431 432 c.Assert(kinds, DeepEquals, expected) 433 } 434 435 func verifyUpdateTasks(c *C, opts, discards int, ts *state.TaskSet, st *state.State) { 436 kinds := taskKinds(ts.Tasks()) 437 438 expected := []string{ 439 "prerequisites", 440 "download-snap", 441 "validate-snap", 442 "mount-snap", 443 } 444 expected = append(expected, "run-hook[pre-refresh]") 445 if opts&unlinkBefore != 0 { 446 expected = append(expected, 447 "stop-snap-services", 448 ) 449 } 450 if opts&unlinkBefore != 0 { 451 expected = append(expected, 452 "remove-aliases", 453 "unlink-current-snap", 454 ) 455 } 456 if opts&updatesGadget != 0 { 457 expected = append(expected, "update-gadget-assets") 458 } 459 expected = append(expected, 460 "copy-snap-data", 461 "setup-profiles", 462 "link-snap", 463 ) 464 if opts&maybeCore != 0 { 465 expected = append(expected, "setup-profiles") 466 } 467 expected = append(expected, 468 "auto-connect", 469 "set-auto-aliases", 470 "setup-aliases", 471 "run-hook[post-refresh]", 472 "start-snap-services") 473 474 c.Assert(ts.Tasks()[len(expected)-2].Summary(), Matches, `Run post-refresh hook of .*`) 475 for i := 0; i < discards; i++ { 476 expected = append(expected, 477 "clear-snap", 478 "discard-snap", 479 ) 480 } 481 if opts&cleanupAfter != 0 { 482 expected = append(expected, 483 "cleanup", 484 ) 485 } 486 expected = append(expected, 487 "run-hook[configure]", 488 "run-hook[check-health]", 489 ) 490 if opts&doesReRefresh != 0 { 491 expected = append(expected, "check-rerefresh") 492 } 493 494 c.Assert(kinds, DeepEquals, expected) 495 } 496 497 func verifyLastTasksetIsReRefresh(c *C, tts []*state.TaskSet) { 498 ts := tts[len(tts)-1] 499 c.Assert(ts.Tasks(), HasLen, 1) 500 c.Check(ts.Tasks()[0].Kind(), Equals, "check-rerefresh") 501 } 502 503 func verifyRemoveTasks(c *C, ts *state.TaskSet) { 504 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 505 "stop-snap-services", 506 "run-hook[remove]", 507 "auto-disconnect", 508 "save-snapshot", 509 "remove-aliases", 510 "unlink-snap", 511 "remove-profiles", 512 "clear-snap", 513 "discard-snap", 514 }) 515 verifyStopReason(c, ts, "remove") 516 } 517 518 func verifyCoreRemoveTasks(c *C, ts *state.TaskSet) { 519 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 520 "stop-snap-services", 521 "run-hook[remove]", 522 "auto-disconnect", 523 "remove-aliases", 524 "unlink-snap", 525 "remove-profiles", 526 "clear-snap", 527 "discard-snap", 528 }) 529 verifyStopReason(c, ts, "remove") 530 } 531 532 func checkIsAutoRefresh(c *C, tasks []*state.Task, expected bool) { 533 for _, t := range tasks { 534 if t.Kind() == "download-snap" { 535 var snapsup snapstate.SnapSetup 536 err := t.Get("snap-setup", &snapsup) 537 c.Assert(err, IsNil) 538 c.Check(snapsup.IsAutoRefresh, Equals, expected) 539 return 540 } 541 } 542 c.Fatalf("cannot find download-snap task in %v", tasks) 543 } 544 545 func (s *snapmgrTestSuite) TestLastIndexFindsLast(c *C) { 546 snapst := &snapstate.SnapState{Sequence: []*snap.SideInfo{ 547 {Revision: snap.R(7)}, 548 {Revision: snap.R(11)}, 549 {Revision: snap.R(11)}, 550 }} 551 c.Check(snapst.LastIndex(snap.R(11)), Equals, 2) 552 } 553 554 func (s *snapmgrTestSuite) TestInstallDevModeConfinementFiltering(c *C) { 555 s.state.Lock() 556 defer s.state.Unlock() 557 558 // if a snap is devmode, you can't install it without --devmode 559 opts := &snapstate.RevisionOptions{Channel: "channel-for-devmode"} 560 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 561 c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`) 562 563 // if a snap is devmode, you *can* install it with --devmode 564 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{DevMode: true}) 565 c.Assert(err, IsNil) 566 567 // if a snap is *not* devmode, you can still install it with --devmode 568 opts.Channel = "channel-for-strict" 569 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{DevMode: true}) 570 c.Assert(err, IsNil) 571 } 572 573 func maybeMockClassicSupport(c *C) (restore func()) { 574 if dirs.SupportsClassicConfinement() { 575 return func() {} 576 } 577 578 d := filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/snap") 579 err := os.MkdirAll(d, 0755) 580 c.Assert(err, IsNil) 581 snapSymlink := filepath.Join(dirs.GlobalRootDir, "snap") 582 err = os.Symlink(d, snapSymlink) 583 c.Assert(err, IsNil) 584 585 return func() { os.Remove(snapSymlink) } 586 } 587 588 func (s *snapmgrTestSuite) TestInstallClassicConfinementFiltering(c *C) { 589 restore := maybeMockClassicSupport(c) 590 defer restore() 591 592 s.state.Lock() 593 defer s.state.Unlock() 594 595 // if a snap is classic, you can't install it without --classic 596 opts := &snapstate.RevisionOptions{Channel: "channel-for-classic"} 597 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 598 c.Assert(err, ErrorMatches, `.* requires classic confinement`) 599 600 // if a snap is classic, you *can* install it with --classic 601 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true}) 602 c.Assert(err, IsNil) 603 604 // if a snap is *not* classic, but can install it with --classic which gets ignored 605 opts.Channel = "channel-for-strict" 606 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true}) 607 c.Assert(err, IsNil) 608 } 609 610 func (s *snapmgrTestSuite) TestInstallFailsWhenClassicSnapsAreNotSupported(c *C) { 611 s.state.Lock() 612 defer s.state.Unlock() 613 614 reset := release.MockReleaseInfo(&release.OS{ 615 ID: "fedora", 616 }) 617 defer reset() 618 619 // this needs doing because dirs depends on the release info 620 dirs.SetRootDir(dirs.GlobalRootDir) 621 622 opts := &snapstate.RevisionOptions{Channel: "channel-for-classic"} 623 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true}) 624 c.Assert(err, ErrorMatches, "classic confinement requires snaps under /snap or symlink from /snap to "+dirs.SnapMountDir) 625 } 626 627 func (s *snapmgrTestSuite) TestInstallTasks(c *C) { 628 s.state.Lock() 629 defer s.state.Unlock() 630 631 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 632 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}) 633 c.Assert(err, IsNil) 634 635 verifyInstallTasks(c, 0, 0, ts, s.state) 636 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 637 } 638 639 func (s *snapmgrTestSuite) TestInstallSnapdSnapType(c *C) { 640 restore := snap.MockSnapdSnapID("snapd-id") // id provided by fakeStore 641 defer restore() 642 643 s.state.Lock() 644 defer s.state.Unlock() 645 646 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 647 ts, err := snapstate.Install(context.Background(), s.state, "snapd", opts, 0, snapstate.Flags{}) 648 c.Assert(err, IsNil) 649 650 verifyInstallTasks(c, noConfigure, 0, ts, s.state) 651 652 snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0]) 653 c.Assert(err, IsNil) 654 c.Check(snapsup.Type, Equals, snap.TypeSnapd) 655 } 656 657 func (s *snapmgrTestSuite) TestInstallCohortTasks(c *C) { 658 s.state.Lock() 659 defer s.state.Unlock() 660 661 opts := &snapstate.RevisionOptions{Channel: "some-channel", CohortKey: "what"} 662 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}) 663 c.Assert(err, IsNil) 664 snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0]) 665 c.Assert(err, IsNil) 666 c.Check(snapsup.CohortKey, Equals, "what") 667 668 verifyInstallTasks(c, 0, 0, ts, s.state) 669 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 670 } 671 672 func (s *snapmgrTestSuite) TestInstallWithDeviceContext(c *C) { 673 s.state.Lock() 674 defer s.state.Unlock() 675 676 // unset the global store, it will need to come via the device context 677 snapstate.ReplaceStore(s.state, nil) 678 679 deviceCtx := &snapstatetest.TrivialDeviceContext{CtxStore: s.fakeStore} 680 681 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 682 ts, err := snapstate.InstallWithDeviceContext(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}, deviceCtx, "") 683 c.Assert(err, IsNil) 684 685 verifyInstallTasks(c, 0, 0, ts, s.state) 686 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 687 } 688 689 func (s *snapmgrTestSuite) TestInstallHookNotRunForInstalledSnap(c *C) { 690 s.state.Lock() 691 defer s.state.Unlock() 692 693 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 694 Active: true, 695 Sequence: []*snap.SideInfo{ 696 {RealName: "some-snap", Revision: snap.R(7)}, 697 }, 698 Current: snap.R(7), 699 SnapType: "app", 700 }) 701 702 mockSnap := makeTestSnap(c, `name: some-snap 703 version: 1.0 704 epoch: 1* 705 `) 706 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{}) 707 c.Assert(err, IsNil) 708 709 runHooks := tasksWithKind(ts, "run-hook") 710 // no install hook task 711 c.Assert(taskKinds(runHooks), DeepEquals, []string{ 712 "run-hook[pre-refresh]", 713 "run-hook[post-refresh]", 714 "run-hook[configure]", 715 "run-hook[check-health]", 716 }) 717 } 718 719 type fullFlags struct{ before, change, after, setup snapstate.Flags } 720 721 func (s *snapmgrTestSuite) testRevertTasksFullFlags(flags fullFlags, c *C) { 722 s.state.Lock() 723 defer s.state.Unlock() 724 725 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 726 Active: true, 727 Sequence: []*snap.SideInfo{ 728 {RealName: "some-snap", Revision: snap.R(7)}, 729 {RealName: "some-snap", Revision: snap.R(11)}, 730 }, 731 Flags: flags.before, 732 Current: snap.R(11), 733 SnapType: "app", 734 }) 735 736 ts, err := snapstate.Revert(s.state, "some-snap", flags.change) 737 c.Assert(err, IsNil) 738 739 tasks := ts.Tasks() 740 c.Assert(s.state.TaskCount(), Equals, len(tasks)) 741 c.Assert(taskKinds(tasks), DeepEquals, []string{ 742 "prerequisites", 743 "prepare-snap", 744 "stop-snap-services", 745 "remove-aliases", 746 "unlink-current-snap", 747 "setup-profiles", 748 "link-snap", 749 "auto-connect", 750 "set-auto-aliases", 751 "setup-aliases", 752 "start-snap-services", 753 "run-hook[configure]", 754 "run-hook[check-health]", 755 }) 756 // a revert is a special refresh 757 verifyStopReason(c, ts, "refresh") 758 759 snapsup, err := snapstate.TaskSnapSetup(tasks[0]) 760 c.Assert(err, IsNil) 761 flags.setup.Revert = true 762 c.Check(snapsup.Flags, Equals, flags.setup) 763 c.Check(snapsup.Type, Equals, snap.TypeApp) 764 765 chg := s.state.NewChange("revert", "revert snap") 766 chg.AddAll(ts) 767 768 s.state.Unlock() 769 defer s.se.Stop() 770 s.settle(c) 771 s.state.Lock() 772 773 var snapst snapstate.SnapState 774 err = snapstate.Get(s.state, "some-snap", &snapst) 775 c.Assert(err, IsNil) 776 c.Check(snapst.Flags, Equals, flags.after) 777 } 778 779 func (s *snapmgrTestSuite) testRevertTasks(flags snapstate.Flags, c *C) { 780 s.testRevertTasksFullFlags(fullFlags{before: flags, change: flags, after: flags, setup: flags}, c) 781 } 782 783 func (s *snapmgrTestSuite) TestRevertTasks(c *C) { 784 s.testRevertTasks(snapstate.Flags{}, c) 785 } 786 787 func (s *snapmgrTestSuite) TestRevertTasksFromDevMode(c *C) { 788 // the snap is installed in devmode, but the request to revert does not specify devmode 789 s.testRevertTasksFullFlags(fullFlags{ 790 before: snapstate.Flags{DevMode: true}, // the snap is installed in devmode 791 change: snapstate.Flags{}, // the request to revert does not specify devmode 792 after: snapstate.Flags{DevMode: true}, // the reverted snap is installed in devmode 793 setup: snapstate.Flags{DevMode: true}, // because setup said so 794 }, c) 795 } 796 797 func (s *snapmgrTestSuite) TestRevertTasksFromJailMode(c *C) { 798 // the snap is installed in jailmode, but the request to revert does not specify jailmode 799 s.testRevertTasksFullFlags(fullFlags{ 800 before: snapstate.Flags{JailMode: true}, // the snap is installed in jailmode 801 change: snapstate.Flags{}, // the request to revert does not specify jailmode 802 after: snapstate.Flags{JailMode: true}, // the reverted snap is installed in jailmode 803 setup: snapstate.Flags{JailMode: true}, // because setup said so 804 }, c) 805 } 806 807 func (s *snapmgrTestSuite) TestRevertTasksFromClassic(c *C) { 808 restore := maybeMockClassicSupport(c) 809 defer restore() 810 811 // the snap is installed in classic, but the request to revert does not specify classic 812 s.testRevertTasksFullFlags(fullFlags{ 813 before: snapstate.Flags{Classic: true}, // the snap is installed in classic 814 change: snapstate.Flags{}, // the request to revert does not specify classic 815 after: snapstate.Flags{Classic: true}, // the reverted snap is installed in classic 816 setup: snapstate.Flags{Classic: true}, // because setup said so 817 }, c) 818 } 819 820 func (s *snapmgrTestSuite) TestRevertTasksDevMode(c *C) { 821 s.testRevertTasks(snapstate.Flags{DevMode: true}, c) 822 } 823 824 func (s *snapmgrTestSuite) TestRevertTasksJailMode(c *C) { 825 s.testRevertTasks(snapstate.Flags{JailMode: true}, c) 826 } 827 828 func (s *snapmgrTestSuite) TestRevertTasksClassic(c *C) { 829 restore := maybeMockClassicSupport(c) 830 defer restore() 831 832 s.testRevertTasks(snapstate.Flags{Classic: true}, c) 833 } 834 835 func (s *snapmgrTestSuite) TestUpdateCreatesGCTasks(c *C) { 836 restore := release.MockOnClassic(false) 837 defer restore() 838 839 s.testUpdateCreatesGCTasks(c, 2) 840 } 841 842 func (s *snapmgrTestSuite) TestUpdateCreatesGCTasksOnClassic(c *C) { 843 restore := release.MockOnClassic(true) 844 defer restore() 845 846 s.testUpdateCreatesGCTasks(c, 3) 847 } 848 849 func (s *snapmgrTestSuite) testUpdateCreatesGCTasks(c *C, expectedDiscards int) { 850 s.state.Lock() 851 defer s.state.Unlock() 852 853 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 854 Active: true, 855 Sequence: []*snap.SideInfo{ 856 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 857 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 858 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 859 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}, 860 }, 861 Current: snap.R(4), 862 SnapType: "app", 863 }) 864 865 ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{}) 866 c.Assert(err, IsNil) 867 868 // ensure edges information is still there 869 te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge) 870 c.Assert(te, NotNil) 871 c.Assert(err, IsNil) 872 873 verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, expectedDiscards, ts, s.state) 874 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 875 } 876 877 func (s snapmgrTestSuite) TestInstallFailsOnDisabledSnap(c *C) { 878 s.state.Lock() 879 defer s.state.Unlock() 880 881 snapst := &snapstate.SnapState{ 882 Active: false, 883 Channel: "channel", 884 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}}, 885 Current: snap.R(2), 886 SnapType: "app", 887 } 888 snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}} 889 _, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "") 890 c.Assert(err, NotNil) 891 c.Assert(err, ErrorMatches, `cannot update disabled snap "some-snap"`) 892 } 893 894 func (s snapmgrTestSuite) TestInstallFailsOnBusySnap(c *C) { 895 s.state.Lock() 896 defer s.state.Unlock() 897 898 // With the refresh-app-awareness feature enabled. 899 tr := config.NewTransaction(s.state) 900 tr.Set("core", "experimental.refresh-app-awareness", true) 901 tr.Commit() 902 903 // With a snap state indicating a snap is already installed. 904 snapst := &snapstate.SnapState{ 905 Active: true, 906 Sequence: []*snap.SideInfo{ 907 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 908 }, 909 Current: snap.R(1), 910 SnapType: "app", 911 } 912 snapstate.Set(s.state, "some-snap", snapst) 913 914 // With a snap info indicating it has an application called "app" 915 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 916 if name != "some-snap" { 917 return s.fakeBackend.ReadInfo(name, si) 918 } 919 info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp} 920 info.Apps = map[string]*snap.AppInfo{ 921 "app": {Snap: info, Name: "app"}, 922 } 923 return info, nil 924 }) 925 // And with cgroup v1 information indicating the app has a process with pid 1234. 926 writePids(c, filepath.Join(dirs.PidsCgroupDir, "snap.some-snap.app"), []int{1234}) 927 928 // Attempt to install revision 2 of the snap. 929 snapsup := &snapstate.SnapSetup{ 930 SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 931 } 932 933 // And observe that we cannot refresh because the snap is busy. 934 _, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "") 935 c.Assert(err, ErrorMatches, `snap "some-snap" has running apps \(app\)`) 936 937 // The state records the time of the failed refresh operation. 938 err = snapstate.Get(s.state, "some-snap", snapst) 939 c.Assert(err, IsNil) 940 c.Check(snapst.RefreshInhibitedTime, NotNil) 941 } 942 943 func (s snapmgrTestSuite) TestInstallDespiteBusySnap(c *C) { 944 s.state.Lock() 945 defer s.state.Unlock() 946 947 // With the refresh-app-awareness feature enabled. 948 tr := config.NewTransaction(s.state) 949 tr.Set("core", "experimental.refresh-app-awareness", true) 950 tr.Commit() 951 952 // With a snap state indicating a snap is already installed and it failed 953 // to refresh over a week ago. Use UTC and Round to have predictable 954 // behaviour across time-zones and with enough precision loss to be 955 // compatible with the serialization format. 956 var longAgo = time.Now().UTC().Round(time.Second).Add(-time.Hour * 24 * 8) 957 snapst := &snapstate.SnapState{ 958 Active: true, 959 Sequence: []*snap.SideInfo{ 960 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 961 }, 962 Current: snap.R(1), 963 SnapType: "app", 964 RefreshInhibitedTime: &longAgo, 965 } 966 snapstate.Set(s.state, "some-snap", snapst) 967 968 // With a snap info indicating it has an application called "app" 969 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 970 if name != "some-snap" { 971 return s.fakeBackend.ReadInfo(name, si) 972 } 973 info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp} 974 info.Apps = map[string]*snap.AppInfo{ 975 "app": {Snap: info, Name: "app"}, 976 } 977 return info, nil 978 }) 979 // And with cgroup v1 information indicating the app has a process with pid 1234. 980 writePids(c, filepath.Join(dirs.PidsCgroupDir, "snap.some-snap.app"), []int{1234}) 981 982 // Attempt to install revision 2 of the snap. 983 snapsup := &snapstate.SnapSetup{ 984 SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 985 } 986 987 // And observe that refresh occurred regardless of the running process. 988 _, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "") 989 c.Assert(err, IsNil) 990 } 991 992 func (s snapmgrTestSuite) TestInstallFailsOnSystem(c *C) { 993 s.state.Lock() 994 defer s.state.Unlock() 995 996 snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "system", SnapID: "some-snap-id", Revision: snap.R(1)}} 997 _, err := snapstate.DoInstall(s.state, nil, snapsup, 0, "") 998 c.Assert(err, NotNil) 999 c.Assert(err, ErrorMatches, `cannot install reserved snap name 'system'`) 1000 } 1001 1002 func (s *snapmgrTestSuite) TestUpdateCreatesDiscardAfterCurrentTasks(c *C) { 1003 s.state.Lock() 1004 defer s.state.Unlock() 1005 1006 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1007 Active: true, 1008 Sequence: []*snap.SideInfo{ 1009 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1010 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 1011 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 1012 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}, 1013 }, 1014 Current: snap.R(1), 1015 SnapType: "app", 1016 }) 1017 1018 ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{}) 1019 c.Assert(err, IsNil) 1020 1021 verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 3, ts, s.state) 1022 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 1023 } 1024 1025 func (s *snapmgrTestSuite) TestUpdateManyTooEarly(c *C) { 1026 s.state.Lock() 1027 defer s.state.Unlock() 1028 1029 s.state.Set("seeded", nil) 1030 1031 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1032 Active: true, 1033 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 1034 Current: snap.R(7), 1035 SnapType: "app", 1036 }) 1037 1038 _, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil) 1039 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 1040 c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`) 1041 } 1042 1043 func (s *snapmgrTestSuite) TestUpdateMany(c *C) { 1044 s.state.Lock() 1045 defer s.state.Unlock() 1046 1047 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1048 Active: true, 1049 Sequence: []*snap.SideInfo{ 1050 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1051 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 1052 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 1053 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}, 1054 }, 1055 Current: snap.R(1), 1056 SnapType: "app", 1057 }) 1058 1059 updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil) 1060 c.Assert(err, IsNil) 1061 c.Assert(tts, HasLen, 2) 1062 verifyLastTasksetIsReRefresh(c, tts) 1063 c.Check(updates, DeepEquals, []string{"some-snap"}) 1064 1065 ts := tts[0] 1066 verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, ts, s.state) 1067 1068 // check that the tasks are in non-default lane 1069 for _, t := range ts.Tasks() { 1070 c.Assert(t.Lanes(), DeepEquals, []int{1}) 1071 } 1072 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())+1) // 1==rerefresh 1073 1074 // ensure edges information is still there 1075 te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge) 1076 c.Assert(te, NotNil) 1077 c.Assert(err, IsNil) 1078 1079 checkIsAutoRefresh(c, ts.Tasks(), false) 1080 } 1081 1082 func (s *snapmgrTestSuite) TestParallelInstanceUpdateMany(c *C) { 1083 restore := release.MockOnClassic(false) 1084 defer restore() 1085 1086 s.state.Lock() 1087 defer s.state.Unlock() 1088 1089 tr := config.NewTransaction(s.state) 1090 tr.Set("core", "experimental.parallel-instances", true) 1091 tr.Commit() 1092 1093 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1094 Active: true, 1095 Sequence: []*snap.SideInfo{ 1096 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1097 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 1098 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 1099 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}, 1100 }, 1101 Current: snap.R(1), 1102 SnapType: "app", 1103 }) 1104 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 1105 Active: true, 1106 Sequence: []*snap.SideInfo{ 1107 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1108 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 1109 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 1110 }, 1111 Current: snap.R(3), 1112 SnapType: "app", 1113 InstanceKey: "instance", 1114 }) 1115 1116 updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil) 1117 c.Assert(err, IsNil) 1118 c.Assert(tts, HasLen, 3) 1119 verifyLastTasksetIsReRefresh(c, tts) 1120 // ensure stable ordering of updates list 1121 if updates[0] != "some-snap" { 1122 updates[1], updates[0] = updates[0], updates[1] 1123 } 1124 1125 c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"}) 1126 1127 var snapsup, snapsupInstance *snapstate.SnapSetup 1128 1129 // ensure stable ordering of task sets list 1130 snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0]) 1131 c.Assert(err, IsNil) 1132 if snapsup.InstanceName() != "some-snap" { 1133 tts[0], tts[1] = tts[1], tts[0] 1134 snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0]) 1135 c.Assert(err, IsNil) 1136 } 1137 snapsupInstance, err = snapstate.TaskSnapSetup(tts[1].Tasks()[0]) 1138 c.Assert(err, IsNil) 1139 1140 c.Assert(snapsup.InstanceName(), Equals, "some-snap") 1141 c.Assert(snapsupInstance.InstanceName(), Equals, "some-snap_instance") 1142 1143 verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, tts[0], s.state) 1144 verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 1, tts[1], s.state) 1145 } 1146 1147 func (s *snapmgrTestSuite) TestUpdateManyDevModeConfinementFiltering(c *C) { 1148 s.state.Lock() 1149 defer s.state.Unlock() 1150 1151 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1152 Active: true, 1153 Channel: "channel-for-devmode", 1154 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 1155 Current: snap.R(7), 1156 SnapType: "app", 1157 }) 1158 1159 // updated snap is devmode, updatemany doesn't update it 1160 _, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil) 1161 // FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error) 1162 c.Assert(tts, HasLen, 0) 1163 } 1164 1165 func (s *snapmgrTestSuite) TestUpdateManyClassicConfinementFiltering(c *C) { 1166 restore := maybeMockClassicSupport(c) 1167 defer restore() 1168 1169 s.state.Lock() 1170 defer s.state.Unlock() 1171 1172 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1173 Active: true, 1174 Channel: "channel-for-classic", 1175 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 1176 Current: snap.R(7), 1177 SnapType: "app", 1178 }) 1179 1180 // if a snap installed without --classic gets a classic update it isn't installed 1181 _, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil) 1182 // FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error) 1183 c.Assert(tts, HasLen, 0) 1184 } 1185 1186 func (s *snapmgrTestSuite) TestUpdateManyClassic(c *C) { 1187 restore := maybeMockClassicSupport(c) 1188 defer restore() 1189 1190 s.state.Lock() 1191 defer s.state.Unlock() 1192 1193 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1194 Active: true, 1195 Channel: "channel-for-classic", 1196 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 1197 Current: snap.R(7), 1198 SnapType: "app", 1199 Flags: snapstate.Flags{Classic: true}, 1200 }) 1201 1202 // snap installed with classic: refresh gets classic 1203 _, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil) 1204 c.Assert(err, IsNil) 1205 c.Assert(tts, HasLen, 2) 1206 verifyLastTasksetIsReRefresh(c, tts) 1207 } 1208 1209 func (s *snapmgrTestSuite) TestUpdateManyDevMode(c *C) { 1210 s.state.Lock() 1211 defer s.state.Unlock() 1212 1213 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1214 Active: true, 1215 Flags: snapstate.Flags{DevMode: true}, 1216 Sequence: []*snap.SideInfo{ 1217 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1218 }, 1219 Current: snap.R(1), 1220 SnapType: "app", 1221 }) 1222 1223 updates, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil) 1224 c.Assert(err, IsNil) 1225 c.Check(updates, HasLen, 1) 1226 } 1227 1228 func (s *snapmgrTestSuite) TestUpdateAllDevMode(c *C) { 1229 s.state.Lock() 1230 defer s.state.Unlock() 1231 1232 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1233 Active: true, 1234 Flags: snapstate.Flags{DevMode: true}, 1235 Sequence: []*snap.SideInfo{ 1236 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1237 }, 1238 Current: snap.R(1), 1239 SnapType: "app", 1240 }) 1241 1242 updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil) 1243 c.Assert(err, IsNil) 1244 c.Check(updates, HasLen, 0) 1245 } 1246 1247 func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC16(c *C) { 1248 s.state.Lock() 1249 defer s.state.Unlock() 1250 1251 snapstate.Set(s.state, "core", &snapstate.SnapState{ 1252 Active: true, 1253 Sequence: []*snap.SideInfo{ 1254 {RealName: "core", SnapID: "core-snap-id", Revision: snap.R(1)}, 1255 }, 1256 Current: snap.R(1), 1257 SnapType: "os", 1258 }) 1259 1260 snapstate.Set(s.state, "some-base", &snapstate.SnapState{ 1261 Active: true, 1262 Sequence: []*snap.SideInfo{ 1263 {RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)}, 1264 }, 1265 Current: snap.R(1), 1266 SnapType: "base", 1267 }) 1268 1269 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1270 Active: true, 1271 Sequence: []*snap.SideInfo{ 1272 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1273 }, 1274 Current: snap.R(1), 1275 SnapType: "app", 1276 Channel: "channel-for-base", 1277 }) 1278 1279 updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core", "some-base"}, 0, nil) 1280 c.Assert(err, IsNil) 1281 c.Assert(tts, HasLen, 4) 1282 verifyLastTasksetIsReRefresh(c, tts) 1283 c.Check(updates, HasLen, 3) 1284 1285 // to make TaskSnapSetup work 1286 chg := s.state.NewChange("refresh", "...") 1287 for _, ts := range tts { 1288 chg.AddAll(ts) 1289 } 1290 1291 prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks()) 1292 prereqs := map[string]bool{} 1293 for i, task := range tts[2].Tasks() { 1294 waitTasks := task.WaitTasks() 1295 if i == 0 { 1296 c.Check(len(waitTasks), Equals, prereqTotal) 1297 } else if task.Kind() == "link-snap" { 1298 c.Check(len(waitTasks), Equals, prereqTotal+1) 1299 for _, pre := range waitTasks { 1300 if pre.Kind() == "link-snap" { 1301 snapsup, err := snapstate.TaskSnapSetup(pre) 1302 c.Assert(err, IsNil) 1303 prereqs[snapsup.InstanceName()] = true 1304 } 1305 } 1306 } 1307 } 1308 1309 c.Check(prereqs, DeepEquals, map[string]bool{ 1310 "core": true, 1311 "some-base": true, 1312 }) 1313 } 1314 1315 func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC18(c *C) { 1316 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 1317 defer r() 1318 1319 restore := snap.MockSnapdSnapID("snapd-id") 1320 defer restore() 1321 1322 s.state.Lock() 1323 defer s.state.Unlock() 1324 1325 snapstate.Set(s.state, "core18", &snapstate.SnapState{ 1326 Active: true, 1327 Sequence: []*snap.SideInfo{ 1328 {RealName: "core18", SnapID: "core18-snap-id", Revision: snap.R(1)}, 1329 }, 1330 Current: snap.R(1), 1331 SnapType: "base", 1332 }) 1333 1334 snapstate.Set(s.state, "some-base", &snapstate.SnapState{ 1335 Active: true, 1336 Sequence: []*snap.SideInfo{ 1337 {RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)}, 1338 }, 1339 Current: snap.R(1), 1340 SnapType: "base", 1341 }) 1342 1343 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 1344 Active: true, 1345 Sequence: []*snap.SideInfo{ 1346 {RealName: "snapd", SnapID: "snapd-id", Revision: snap.R(1)}, 1347 }, 1348 Current: snap.R(1), 1349 SnapType: "app", 1350 }) 1351 1352 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1353 Active: true, 1354 Sequence: []*snap.SideInfo{ 1355 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1356 }, 1357 Current: snap.R(1), 1358 SnapType: "app", 1359 Channel: "channel-for-base", 1360 }) 1361 1362 updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core18", "some-base", "snapd"}, 0, nil) 1363 c.Assert(err, IsNil) 1364 c.Assert(tts, HasLen, 5) 1365 verifyLastTasksetIsReRefresh(c, tts) 1366 c.Check(updates, HasLen, 4) 1367 1368 // to make TaskSnapSetup work 1369 chg := s.state.NewChange("refresh", "...") 1370 for _, ts := range tts { 1371 chg.AddAll(ts) 1372 } 1373 1374 // Note that some-app only waits for snapd+some-base. The core18 1375 // base is not special to this snap and not waited for 1376 prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks()) 1377 prereqs := map[string]bool{} 1378 for i, task := range tts[3].Tasks() { 1379 waitTasks := task.WaitTasks() 1380 if i == 0 { 1381 c.Check(len(waitTasks), Equals, prereqTotal) 1382 } else if task.Kind() == "link-snap" { 1383 c.Check(len(waitTasks), Equals, prereqTotal+1) 1384 for _, pre := range waitTasks { 1385 if pre.Kind() == "link-snap" { 1386 snapsup, err := snapstate.TaskSnapSetup(pre) 1387 c.Assert(err, IsNil) 1388 prereqs[snapsup.InstanceName()] = true 1389 } 1390 } 1391 } 1392 } 1393 1394 // Note that "core18" is not part of the prereqs for some-app 1395 // as it does not use this base. 1396 c.Check(prereqs, DeepEquals, map[string]bool{ 1397 "some-base": true, 1398 "snapd": true, 1399 }) 1400 } 1401 1402 func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshes(c *C) { 1403 s.state.Lock() 1404 defer s.state.Unlock() 1405 1406 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1407 Active: true, 1408 Sequence: []*snap.SideInfo{ 1409 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1410 }, 1411 Current: snap.R(1), 1412 SnapType: "app", 1413 }) 1414 1415 validateCalled := false 1416 validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) { 1417 validateCalled = true 1418 c.Check(refreshes, HasLen, 1) 1419 c.Check(refreshes[0].InstanceName(), Equals, "some-snap") 1420 c.Check(refreshes[0].SnapID, Equals, "some-snap-id") 1421 c.Check(refreshes[0].Revision, Equals, snap.R(11)) 1422 c.Check(ignoreValidation, HasLen, 0) 1423 return refreshes, nil 1424 } 1425 // hook it up 1426 snapstate.ValidateRefreshes = validateRefreshes 1427 1428 updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil) 1429 c.Assert(err, IsNil) 1430 c.Assert(tts, HasLen, 2) 1431 verifyLastTasksetIsReRefresh(c, tts) 1432 c.Check(updates, DeepEquals, []string{"some-snap"}) 1433 verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state) 1434 1435 c.Check(validateCalled, Equals, true) 1436 } 1437 1438 func (s *snapmgrTestSuite) TestParallelInstanceUpdateManyValidateRefreshes(c *C) { 1439 s.state.Lock() 1440 defer s.state.Unlock() 1441 1442 tr := config.NewTransaction(s.state) 1443 tr.Set("core", "experimental.parallel-instances", true) 1444 tr.Commit() 1445 1446 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1447 Active: true, 1448 Sequence: []*snap.SideInfo{ 1449 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1450 }, 1451 Current: snap.R(1), 1452 SnapType: "app", 1453 }) 1454 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 1455 Active: true, 1456 Sequence: []*snap.SideInfo{ 1457 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1458 }, 1459 Current: snap.R(1), 1460 SnapType: "app", 1461 InstanceKey: "instance", 1462 }) 1463 1464 validateCalled := false 1465 validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) { 1466 validateCalled = true 1467 c.Check(refreshes, HasLen, 2) 1468 instanceIdx := 0 1469 someIdx := 1 1470 if refreshes[0].InstanceName() != "some-snap_instance" { 1471 instanceIdx = 1 1472 someIdx = 0 1473 } 1474 c.Check(refreshes[someIdx].InstanceName(), Equals, "some-snap") 1475 c.Check(refreshes[instanceIdx].InstanceName(), Equals, "some-snap_instance") 1476 c.Check(refreshes[0].SnapID, Equals, "some-snap-id") 1477 c.Check(refreshes[0].Revision, Equals, snap.R(11)) 1478 c.Check(refreshes[1].SnapID, Equals, "some-snap-id") 1479 c.Check(refreshes[1].Revision, Equals, snap.R(11)) 1480 c.Check(ignoreValidation, HasLen, 0) 1481 return refreshes, nil 1482 } 1483 // hook it up 1484 snapstate.ValidateRefreshes = validateRefreshes 1485 1486 updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil) 1487 c.Assert(err, IsNil) 1488 c.Assert(tts, HasLen, 3) 1489 verifyLastTasksetIsReRefresh(c, tts) 1490 sort.Strings(updates) 1491 c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"}) 1492 verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state) 1493 verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[1], s.state) 1494 1495 c.Check(validateCalled, Equals, true) 1496 } 1497 1498 func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshesUnhappy(c *C) { 1499 s.state.Lock() 1500 defer s.state.Unlock() 1501 1502 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1503 Active: true, 1504 Sequence: []*snap.SideInfo{ 1505 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1506 }, 1507 Current: snap.R(1), 1508 }) 1509 1510 validateErr := errors.New("refresh control error") 1511 validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) { 1512 c.Check(refreshes, HasLen, 1) 1513 c.Check(refreshes[0].SnapID, Equals, "some-snap-id") 1514 c.Check(refreshes[0].Revision, Equals, snap.R(11)) 1515 c.Check(ignoreValidation, HasLen, 0) 1516 return nil, validateErr 1517 } 1518 // hook it up 1519 snapstate.ValidateRefreshes = validateRefreshes 1520 1521 // refresh all => no error 1522 updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil) 1523 c.Assert(err, IsNil) 1524 c.Check(tts, HasLen, 0) 1525 c.Check(updates, HasLen, 0) 1526 1527 // refresh some-snap => report error 1528 updates, tts, err = snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil) 1529 c.Assert(err, Equals, validateErr) 1530 c.Check(tts, HasLen, 0) 1531 c.Check(updates, HasLen, 0) 1532 1533 } 1534 1535 func (s *snapmgrTestSuite) TestRevertCreatesNoGCTasks(c *C) { 1536 s.state.Lock() 1537 defer s.state.Unlock() 1538 1539 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1540 Active: true, 1541 SnapType: "app", 1542 Sequence: []*snap.SideInfo{ 1543 {RealName: "some-snap", Revision: snap.R(1)}, 1544 {RealName: "some-snap", Revision: snap.R(2)}, 1545 {RealName: "some-snap", Revision: snap.R(3)}, 1546 {RealName: "some-snap", Revision: snap.R(4)}, 1547 }, 1548 Current: snap.R(2), 1549 }) 1550 1551 ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(4), snapstate.Flags{}) 1552 c.Assert(err, IsNil) 1553 1554 // ensure that we do not run any form of garbage-collection 1555 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 1556 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 1557 "prerequisites", 1558 "prepare-snap", 1559 "stop-snap-services", 1560 "remove-aliases", 1561 "unlink-current-snap", 1562 "setup-profiles", 1563 "link-snap", 1564 "auto-connect", 1565 "set-auto-aliases", 1566 "setup-aliases", 1567 "start-snap-services", 1568 "run-hook[configure]", 1569 "run-hook[check-health]", 1570 }) 1571 } 1572 1573 func (s *snapmgrTestSuite) TestEnableTasks(c *C) { 1574 s.state.Lock() 1575 defer s.state.Unlock() 1576 1577 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1578 Sequence: []*snap.SideInfo{ 1579 {RealName: "some-snap", Revision: snap.R(11)}, 1580 }, 1581 Current: snap.R(11), 1582 Active: false, 1583 }) 1584 1585 ts, err := snapstate.Enable(s.state, "some-snap") 1586 c.Assert(err, IsNil) 1587 1588 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 1589 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 1590 "prepare-snap", 1591 "setup-profiles", 1592 "link-snap", 1593 "setup-aliases", 1594 "start-snap-services", 1595 }) 1596 } 1597 1598 func (s *snapmgrTestSuite) TestSwitchTasks(c *C) { 1599 s.state.Lock() 1600 defer s.state.Unlock() 1601 1602 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1603 Sequence: []*snap.SideInfo{ 1604 {RealName: "some-snap", Revision: snap.R(11)}, 1605 }, 1606 Current: snap.R(11), 1607 Active: false, 1608 }) 1609 1610 ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}) 1611 c.Assert(err, IsNil) 1612 1613 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 1614 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{"switch-snap"}) 1615 } 1616 1617 func (s *snapmgrTestSuite) TestSwitchConflict(c *C) { 1618 s.state.Lock() 1619 defer s.state.Unlock() 1620 1621 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1622 Sequence: []*snap.SideInfo{ 1623 {RealName: "some-snap", Revision: snap.R(11)}, 1624 }, 1625 Current: snap.R(11), 1626 Active: false, 1627 }) 1628 1629 ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}) 1630 c.Assert(err, IsNil) 1631 // need a change to make the tasks visible 1632 s.state.NewChange("switch-snap", "...").AddAll(ts) 1633 1634 _, err = snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "other-channel"}) 1635 c.Check(err, ErrorMatches, `snap "some-snap" has "switch-snap" change in progress`) 1636 } 1637 1638 func (s *snapmgrTestSuite) TestSwitchUnhappy(c *C) { 1639 s.state.Lock() 1640 defer s.state.Unlock() 1641 1642 _, err := snapstate.Switch(s.state, "non-existing-snap", &snapstate.RevisionOptions{Channel: "some-channel"}) 1643 c.Assert(err, ErrorMatches, `snap "non-existing-snap" is not installed`) 1644 } 1645 1646 func (s *snapmgrTestSuite) TestSwitchRevision(c *C) { 1647 s.state.Lock() 1648 defer s.state.Unlock() 1649 1650 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1651 Sequence: []*snap.SideInfo{ 1652 {RealName: "some-snap", Revision: snap.R(11)}, 1653 }, 1654 Current: snap.R(11), 1655 }) 1656 1657 _, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(42)}) 1658 c.Assert(err, ErrorMatches, "cannot switch revision") 1659 } 1660 1661 func (s *snapmgrTestSuite) TestSwitchKernelTrackForbidden(c *C) { 1662 s.state.Lock() 1663 defer s.state.Unlock() 1664 1665 r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 1666 defer r() 1667 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 1668 Sequence: []*snap.SideInfo{ 1669 {RealName: "kernel", Revision: snap.R(11)}, 1670 }, 1671 Channel: "18/stable", 1672 Current: snap.R(11), 1673 Active: true, 1674 }) 1675 1676 _, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}) 1677 c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`) 1678 } 1679 1680 func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyIsOK(c *C) { 1681 s.state.Lock() 1682 defer s.state.Unlock() 1683 1684 r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 1685 defer r() 1686 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 1687 Sequence: []*snap.SideInfo{ 1688 {RealName: "kernel", Revision: snap.R(11)}, 1689 }, 1690 Channel: "18/stable", 1691 Current: snap.R(11), 1692 Active: true, 1693 }) 1694 1695 _, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "18/beta"}) 1696 c.Assert(err, IsNil) 1697 } 1698 1699 func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyDefaultTrackIsOK(c *C) { 1700 s.state.Lock() 1701 defer s.state.Unlock() 1702 1703 r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 1704 defer r() 1705 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 1706 Sequence: []*snap.SideInfo{ 1707 {RealName: "kernel", Revision: snap.R(11)}, 1708 }, 1709 Channel: "18/stable", 1710 Current: snap.R(11), 1711 Active: true, 1712 }) 1713 1714 _, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "beta"}) 1715 c.Assert(err, IsNil) 1716 } 1717 1718 func (s *snapmgrTestSuite) TestSwitchGadgetTrackForbidden(c *C) { 1719 s.state.Lock() 1720 defer s.state.Unlock() 1721 1722 r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18")) 1723 defer r() 1724 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 1725 Sequence: []*snap.SideInfo{ 1726 {RealName: "brand-gadget", Revision: snap.R(11)}, 1727 }, 1728 Channel: "18/stable", 1729 Current: snap.R(11), 1730 Active: true, 1731 }) 1732 1733 _, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}) 1734 c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`) 1735 } 1736 1737 func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyIsOK(c *C) { 1738 s.state.Lock() 1739 defer s.state.Unlock() 1740 1741 r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18")) 1742 defer r() 1743 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 1744 Sequence: []*snap.SideInfo{ 1745 {RealName: "brand-gadget", Revision: snap.R(11)}, 1746 }, 1747 Channel: "18/stable", 1748 Current: snap.R(11), 1749 Active: true, 1750 }) 1751 1752 _, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "18/beta"}) 1753 c.Assert(err, IsNil) 1754 } 1755 1756 func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyDefaultTrackIsOK(c *C) { 1757 s.state.Lock() 1758 defer s.state.Unlock() 1759 1760 r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18")) 1761 defer r() 1762 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 1763 Sequence: []*snap.SideInfo{ 1764 {RealName: "brand-gadget", Revision: snap.R(11)}, 1765 }, 1766 Channel: "18/stable", 1767 Current: snap.R(11), 1768 Active: true, 1769 }) 1770 1771 _, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "beta"}) 1772 c.Assert(err, IsNil) 1773 } 1774 1775 func (s *snapmgrTestSuite) TestDisableTasks(c *C) { 1776 s.state.Lock() 1777 defer s.state.Unlock() 1778 1779 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1780 Sequence: []*snap.SideInfo{ 1781 {RealName: "some-snap", Revision: snap.R(11)}, 1782 }, 1783 Current: snap.R(11), 1784 Active: true, 1785 }) 1786 1787 ts, err := snapstate.Disable(s.state, "some-snap") 1788 c.Assert(err, IsNil) 1789 1790 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 1791 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 1792 "stop-snap-services", 1793 "remove-aliases", 1794 "unlink-snap", 1795 "remove-profiles", 1796 }) 1797 verifyStopReason(c, ts, "disable") 1798 } 1799 1800 func (s *snapmgrTestSuite) TestEnableConflict(c *C) { 1801 s.state.Lock() 1802 defer s.state.Unlock() 1803 1804 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1805 Sequence: []*snap.SideInfo{ 1806 {RealName: "some-snap", Revision: snap.R(11)}, 1807 }, 1808 Current: snap.R(11), 1809 Active: false, 1810 }) 1811 1812 ts, err := snapstate.Enable(s.state, "some-snap") 1813 c.Assert(err, IsNil) 1814 // need a change to make the tasks visible 1815 s.state.NewChange("enable", "...").AddAll(ts) 1816 1817 _, err = snapstate.Enable(s.state, "some-snap") 1818 c.Assert(err, ErrorMatches, `snap "some-snap" has "enable" change in progress`) 1819 } 1820 1821 func (s *snapmgrTestSuite) TestDisableConflict(c *C) { 1822 s.state.Lock() 1823 defer s.state.Unlock() 1824 1825 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1826 Sequence: []*snap.SideInfo{ 1827 {RealName: "some-snap", Revision: snap.R(11)}, 1828 }, 1829 Current: snap.R(11), 1830 Active: true, 1831 }) 1832 1833 ts, err := snapstate.Disable(s.state, "some-snap") 1834 c.Assert(err, IsNil) 1835 // need a change to make the tasks visible 1836 s.state.NewChange("install", "...").AddAll(ts) 1837 1838 _, err = snapstate.Disable(s.state, "some-snap") 1839 c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`) 1840 } 1841 1842 func (s *snapmgrTestSuite) TestDoInstallWithSlots(c *C) { 1843 s.state.Lock() 1844 defer s.state.Unlock() 1845 1846 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 1847 1848 ts, err := snapstate.Install(context.Background(), s.state, "snap-content-slot", nil, 0, snapstate.Flags{}) 1849 c.Assert(err, IsNil) 1850 1851 var snapsup snapstate.SnapSetup 1852 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 1853 c.Assert(err, IsNil) 1854 1855 c.Check(snapsup.PlugsOnly, Equals, false) 1856 } 1857 1858 func (s *snapmgrTestSuite) TestDoUpdateHadSlots(c *C) { 1859 s.state.Lock() 1860 defer s.state.Unlock() 1861 1862 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1863 Active: true, 1864 Sequence: []*snap.SideInfo{ 1865 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}, 1866 }, 1867 Current: snap.R(4), 1868 SnapType: "app", 1869 }) 1870 1871 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 1872 if name != "some-snap" { 1873 return s.fakeBackend.ReadInfo(name, si) 1874 } 1875 1876 info := &snap.Info{ 1877 SideInfo: *si, 1878 SnapType: snap.TypeApp, 1879 } 1880 info.Slots = map[string]*snap.SlotInfo{ 1881 "some-slot": { 1882 Snap: info, 1883 Name: "shared-content", 1884 Interface: "content", 1885 Attrs: map[string]interface{}{ 1886 "content": "shared-content", 1887 }, 1888 }, 1889 } 1890 return info, nil 1891 }) 1892 1893 ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{}) 1894 c.Assert(err, IsNil) 1895 1896 var snapsup snapstate.SnapSetup 1897 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 1898 c.Assert(err, IsNil) 1899 1900 c.Check(snapsup.PlugsOnly, Equals, false) 1901 } 1902 1903 func (s *snapmgrTestSuite) TestDoInstallChannelDefault(c *C) { 1904 s.state.Lock() 1905 defer s.state.Unlock() 1906 1907 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 1908 c.Assert(err, IsNil) 1909 1910 var snapsup snapstate.SnapSetup 1911 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 1912 c.Assert(err, IsNil) 1913 1914 c.Check(snapsup.Channel, Equals, "stable") 1915 } 1916 1917 func (s *snapmgrTestSuite) TestInstallRevision(c *C) { 1918 s.state.Lock() 1919 defer s.state.Unlock() 1920 1921 opts := &snapstate.RevisionOptions{Revision: snap.R(7)} 1922 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}) 1923 c.Assert(err, IsNil) 1924 1925 var snapsup snapstate.SnapSetup 1926 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 1927 c.Assert(err, IsNil) 1928 1929 c.Check(snapsup.Revision(), Equals, snap.R(7)) 1930 } 1931 1932 func (s *snapmgrTestSuite) TestInstallTooEarly(c *C) { 1933 s.state.Lock() 1934 defer s.state.Unlock() 1935 1936 s.state.Set("seeded", nil) 1937 1938 _, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 1939 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 1940 c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`) 1941 } 1942 1943 func (s *snapmgrTestSuite) TestInstallConflict(c *C) { 1944 s.state.Lock() 1945 defer s.state.Unlock() 1946 1947 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 1948 c.Assert(err, IsNil) 1949 // need a change to make the tasks visible 1950 s.state.NewChange("install", "...").AddAll(ts) 1951 1952 _, err = snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 1953 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 1954 c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`) 1955 } 1956 1957 func (s *snapmgrTestSuite) TestInstallAliasConflict(c *C) { 1958 s.state.Lock() 1959 defer s.state.Unlock() 1960 1961 snapstate.Set(s.state, "otherfoosnap", &snapstate.SnapState{ 1962 Sequence: []*snap.SideInfo{ 1963 {RealName: "otherfoosnap", Revision: snap.R(30)}, 1964 }, 1965 Current: snap.R(30), 1966 Active: true, 1967 Aliases: map[string]*snapstate.AliasTarget{ 1968 "foo.bar": {Manual: "bar"}, 1969 }, 1970 SnapType: "app", 1971 }) 1972 1973 _, err := snapstate.Install(context.Background(), s.state, "foo", nil, 0, snapstate.Flags{}) 1974 c.Assert(err, ErrorMatches, `snap "foo" command namespace conflicts with alias "foo\.bar" for "otherfoosnap" snap`) 1975 } 1976 1977 func (s *snapmgrTestSuite) TestInstallStrictIgnoresClassic(c *C) { 1978 restore := maybeMockClassicSupport(c) 1979 defer restore() 1980 1981 s.state.Lock() 1982 defer s.state.Unlock() 1983 1984 opts := &snapstate.RevisionOptions{Channel: "channel-for-strict"} 1985 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true}) 1986 c.Assert(err, IsNil) 1987 1988 c.Assert(err, IsNil) 1989 1990 chg := s.state.NewChange("install", "install snap") 1991 chg.AddAll(ts) 1992 1993 s.state.Unlock() 1994 defer s.se.Stop() 1995 s.settle(c) 1996 s.state.Lock() 1997 1998 c.Assert(chg.Err(), IsNil) 1999 c.Assert(chg.IsReady(), Equals, true) 2000 2001 // verify snap is *not* classic 2002 var snapst snapstate.SnapState 2003 err = snapstate.Get(s.state, "some-snap", &snapst) 2004 c.Assert(err, IsNil) 2005 c.Check(snapst.Channel, Equals, "channel-for-strict") 2006 c.Check(snapst.Classic, Equals, false) 2007 } 2008 2009 // A sneakyStore changes the state when called 2010 type sneakyStore struct { 2011 *fakeStore 2012 state *state.State 2013 } 2014 2015 func (s sneakyStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) { 2016 s.state.Lock() 2017 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2018 Active: true, 2019 Channel: "edge", 2020 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}}, 2021 Current: snap.R(1), 2022 SnapType: "app", 2023 }) 2024 s.state.Unlock() 2025 return s.fakeStore.SnapAction(ctx, currentSnaps, actions, user, opts) 2026 } 2027 2028 func (s *snapmgrTestSuite) TestInstallStateConflict(c *C) { 2029 s.state.Lock() 2030 defer s.state.Unlock() 2031 2032 snapstate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state}) 2033 2034 _, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 2035 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 2036 c.Assert(err, ErrorMatches, `snap "some-snap" has changes in progress`) 2037 } 2038 2039 func (s *snapmgrTestSuite) TestInstallPathTooEarly(c *C) { 2040 s.state.Lock() 2041 defer s.state.Unlock() 2042 2043 r := snapstatetest.MockDeviceModel(nil) 2044 defer r() 2045 2046 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0") 2047 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{}) 2048 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 2049 c.Assert(err, ErrorMatches, `too early for operation, device model not yet acknowledged`) 2050 2051 } 2052 2053 func (s *snapmgrTestSuite) TestInstallPathConflict(c *C) { 2054 s.state.Lock() 2055 defer s.state.Unlock() 2056 2057 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 2058 c.Assert(err, IsNil) 2059 // need a change to make the tasks visible 2060 s.state.NewChange("install", "...").AddAll(ts) 2061 2062 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0") 2063 _, _, err = snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{}) 2064 c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`) 2065 } 2066 2067 func (s *snapmgrTestSuite) TestInstallPathMissingName(c *C) { 2068 s.state.Lock() 2069 defer s.state.Unlock() 2070 2071 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0") 2072 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{}, mockSnap, "", "", snapstate.Flags{}) 2073 c.Assert(err, ErrorMatches, fmt.Sprintf(`internal error: snap name to install %q not provided`, mockSnap)) 2074 } 2075 2076 func (s *snapmgrTestSuite) TestInstallPathSnapIDRevisionUnset(c *C) { 2077 s.state.Lock() 2078 defer s.state.Unlock() 2079 2080 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0") 2081 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "snapididid"}, mockSnap, "", "", snapstate.Flags{}) 2082 c.Assert(err, ErrorMatches, fmt.Sprintf(`internal error: snap id set to install %q but revision is unset`, mockSnap)) 2083 } 2084 2085 func (s *snapmgrTestSuite) TestInstallPathValidateFlags(c *C) { 2086 s.state.Lock() 2087 defer s.state.Unlock() 2088 2089 mockSnap := makeTestSnap(c, `name: some-snap 2090 version: 1.0 2091 confinement: devmode 2092 `) 2093 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{}) 2094 c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`) 2095 } 2096 2097 func (s *snapmgrTestSuite) TestInstallPathStrictIgnoresClassic(c *C) { 2098 restore := maybeMockClassicSupport(c) 2099 defer restore() 2100 2101 s.state.Lock() 2102 defer s.state.Unlock() 2103 2104 mockSnap := makeTestSnap(c, `name: some-snap 2105 version: 1.0 2106 confinement: strict 2107 `) 2108 2109 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{Classic: true}) 2110 c.Assert(err, IsNil) 2111 2112 c.Assert(err, IsNil) 2113 2114 chg := s.state.NewChange("install", "install snap") 2115 chg.AddAll(ts) 2116 2117 s.state.Unlock() 2118 defer s.se.Stop() 2119 s.settle(c) 2120 s.state.Lock() 2121 2122 c.Assert(chg.Err(), IsNil) 2123 c.Assert(chg.IsReady(), Equals, true) 2124 2125 // verify snap is *not* classic 2126 var snapst snapstate.SnapState 2127 err = snapstate.Get(s.state, "some-snap", &snapst) 2128 c.Assert(err, IsNil) 2129 c.Check(snapst.Classic, Equals, false) 2130 } 2131 2132 func (s *snapmgrTestSuite) TestParallelInstanceInstallNotAllowed(c *C) { 2133 s.state.Lock() 2134 defer s.state.Unlock() 2135 2136 snapstate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state}) 2137 2138 tr := config.NewTransaction(s.state) 2139 tr.Set("core", "experimental.parallel-instances", true) 2140 tr.Commit() 2141 2142 _, err := snapstate.Install(context.Background(), s.state, "core_foo", nil, 0, snapstate.Flags{}) 2143 c.Check(err, ErrorMatches, `cannot install snap of type os as "core_foo"`) 2144 2145 _, err = snapstate.Install(context.Background(), s.state, "some-base_foo", nil, 0, snapstate.Flags{}) 2146 c.Check(err, ErrorMatches, `cannot install snap of type base as "some-base_foo"`) 2147 2148 _, err = snapstate.Install(context.Background(), s.state, "some-gadget_foo", nil, 0, snapstate.Flags{}) 2149 c.Check(err, ErrorMatches, `cannot install snap of type gadget as "some-gadget_foo"`) 2150 2151 _, err = snapstate.Install(context.Background(), s.state, "some-kernel_foo", nil, 0, snapstate.Flags{}) 2152 c.Check(err, ErrorMatches, `cannot install snap of type kernel as "some-kernel_foo"`) 2153 2154 _, err = snapstate.Install(context.Background(), s.state, "some-snapd_foo", nil, 0, snapstate.Flags{}) 2155 c.Check(err, ErrorMatches, `cannot install snap of type snapd as "some-snapd_foo"`) 2156 } 2157 2158 func (s *snapmgrTestSuite) TestInstallPathFailsEarlyOnEpochMismatch(c *C) { 2159 s.state.Lock() 2160 defer s.state.Unlock() 2161 2162 // have epoch 1* installed 2163 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2164 Active: true, 2165 Channel: "edge", 2166 Sequence: []*snap.SideInfo{{RealName: "some-snap", Revision: snap.R(7)}}, 2167 Current: snap.R(7), 2168 }) 2169 2170 // try to install epoch 42 2171 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0\nepoch: 42\n") 2172 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{}) 2173 c.Assert(err, ErrorMatches, `cannot refresh "some-snap" to local snap with epoch 42, because it can't read the current epoch of 1\*`) 2174 } 2175 2176 func (s *snapmgrTestSuite) TestUpdateTasksPropagatesErrors(c *C) { 2177 s.state.Lock() 2178 defer s.state.Unlock() 2179 2180 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2181 Active: true, 2182 Channel: "edge", 2183 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "fakestore-please-error-on-refresh", Revision: snap.R(7)}}, 2184 Current: snap.R(7), 2185 }) 2186 2187 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 2188 c.Assert(err, ErrorMatches, `failing as requested`) 2189 } 2190 2191 func (s *snapmgrTestSuite) TestUpdateTasks(c *C) { 2192 s.state.Lock() 2193 defer s.state.Unlock() 2194 2195 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2196 Active: true, 2197 Channel: "edge", 2198 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 2199 Current: snap.R(7), 2200 SnapType: "app", 2201 }) 2202 2203 validateCalled := false 2204 happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) { 2205 validateCalled = true 2206 return refreshes, nil 2207 } 2208 // hook it up 2209 snapstate.ValidateRefreshes = happyValidateRefreshes 2210 2211 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 2212 c.Assert(err, IsNil) 2213 verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state) 2214 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 2215 2216 c.Check(validateCalled, Equals, true) 2217 2218 var snapsup snapstate.SnapSetup 2219 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 2220 c.Assert(err, IsNil) 2221 2222 c.Check(snapsup.Channel, Equals, "some-channel") 2223 } 2224 2225 func (s *snapmgrTestSuite) TestUpdateWithDeviceContext(c *C) { 2226 s.state.Lock() 2227 defer s.state.Unlock() 2228 2229 // unset the global store, it will need to come via the device context 2230 snapstate.ReplaceStore(s.state, nil) 2231 2232 deviceCtx := &snapstatetest.TrivialDeviceContext{ 2233 DeviceModel: DefaultModel(), 2234 CtxStore: s.fakeStore, 2235 } 2236 2237 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2238 Active: true, 2239 Channel: "edge", 2240 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 2241 Current: snap.R(7), 2242 SnapType: "app", 2243 }) 2244 2245 validateCalled := false 2246 happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx1 snapstate.DeviceContext) ([]*snap.Info, error) { 2247 c.Check(deviceCtx1, Equals, deviceCtx) 2248 validateCalled = true 2249 return refreshes, nil 2250 } 2251 // hook it up 2252 snapstate.ValidateRefreshes = happyValidateRefreshes 2253 2254 ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}, deviceCtx, "") 2255 c.Assert(err, IsNil) 2256 verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state) 2257 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 2258 2259 c.Check(validateCalled, Equals, true) 2260 } 2261 2262 func (s *snapmgrTestSuite) TestUpdateWithDeviceContextToRevision(c *C) { 2263 s.state.Lock() 2264 defer s.state.Unlock() 2265 2266 // unset the global store, it will need to come via the device context 2267 snapstate.ReplaceStore(s.state, nil) 2268 2269 deviceCtx := &snapstatetest.TrivialDeviceContext{ 2270 DeviceModel: DefaultModel(), 2271 CtxStore: s.fakeStore, 2272 } 2273 2274 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2275 Active: true, 2276 Sequence: []*snap.SideInfo{ 2277 {RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"}, 2278 }, 2279 Current: snap.R(5), 2280 SnapType: "app", 2281 UserID: 1, 2282 }) 2283 2284 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(11)} 2285 ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", opts, 0, snapstate.Flags{}, deviceCtx, "") 2286 c.Assert(err, IsNil) 2287 verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state) 2288 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 2289 } 2290 2291 func (s *snapmgrTestSuite) TestUpdateTasksCoreSetsIgnoreOnConfigure(c *C) { 2292 s.state.Lock() 2293 defer s.state.Unlock() 2294 2295 snapstate.Set(s.state, "core", &snapstate.SnapState{ 2296 Active: true, 2297 Channel: "edge", 2298 Sequence: []*snap.SideInfo{{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(7)}}, 2299 Current: snap.R(7), 2300 SnapType: "os", 2301 }) 2302 2303 oldConfigure := snapstate.Configure 2304 defer func() { snapstate.Configure = oldConfigure }() 2305 2306 var configureFlags int 2307 snapstate.Configure = func(st *state.State, snapName string, patch map[string]interface{}, flags int) *state.TaskSet { 2308 configureFlags = flags 2309 return state.NewTaskSet() 2310 } 2311 2312 _, err := snapstate.Update(s.state, "core", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 2313 c.Assert(err, IsNil) 2314 2315 // ensure the core snap sets the "ignore-hook-error" flag 2316 c.Check(configureFlags&snapstate.IgnoreHookError, Equals, 1) 2317 } 2318 2319 func (s *snapmgrTestSuite) TestUpdateDevModeConfinementFiltering(c *C) { 2320 restore := maybeMockClassicSupport(c) 2321 defer restore() 2322 2323 s.state.Lock() 2324 defer s.state.Unlock() 2325 2326 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2327 Active: true, 2328 Channel: "channel-for-devmode", 2329 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 2330 Current: snap.R(7), 2331 SnapType: "app", 2332 }) 2333 2334 // updated snap is devmode, refresh without --devmode, do nothing 2335 // TODO: better error message here 2336 _, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{}) 2337 c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`) 2338 2339 // updated snap is devmode, refresh with --devmode 2340 _, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true}) 2341 c.Assert(err, IsNil) 2342 } 2343 2344 func (s *snapmgrTestSuite) TestUpdateClassicConfinementFiltering(c *C) { 2345 restore := maybeMockClassicSupport(c) 2346 defer restore() 2347 2348 s.state.Lock() 2349 defer s.state.Unlock() 2350 2351 snapstate.Set(s.state, "some-snap-now-classic", &snapstate.SnapState{ 2352 Active: true, 2353 Sequence: []*snap.SideInfo{{RealName: "some-snap-now-classic", SnapID: "some-snap-now-classic-id", Revision: snap.R(7)}}, 2354 Current: snap.R(7), 2355 SnapType: "app", 2356 }) 2357 2358 // updated snap is classic, refresh without --classic, do nothing 2359 // TODO: better error message here 2360 _, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{}) 2361 c.Assert(err, ErrorMatches, `.* requires classic confinement`) 2362 2363 // updated snap is classic, refresh with --classic 2364 ts, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{Classic: true}) 2365 c.Assert(err, IsNil) 2366 2367 chg := s.state.NewChange("refresh", "refresh snap") 2368 chg.AddAll(ts) 2369 2370 s.state.Unlock() 2371 defer s.se.Stop() 2372 s.settle(c) 2373 s.state.Lock() 2374 2375 c.Assert(chg.Err(), IsNil) 2376 c.Assert(chg.IsReady(), Equals, true) 2377 2378 // verify snap is in classic 2379 var snapst snapstate.SnapState 2380 err = snapstate.Get(s.state, "some-snap-now-classic", &snapst) 2381 c.Assert(err, IsNil) 2382 c.Check(snapst.Classic, Equals, true) 2383 } 2384 2385 func (s *snapmgrTestSuite) TestUpdateClassicFromClassic(c *C) { 2386 restore := maybeMockClassicSupport(c) 2387 defer restore() 2388 2389 s.state.Lock() 2390 defer s.state.Unlock() 2391 2392 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2393 Active: true, 2394 Channel: "channel-for-classic", 2395 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 2396 Current: snap.R(7), 2397 SnapType: "app", 2398 Flags: snapstate.Flags{Classic: true}, 2399 }) 2400 2401 // snap installed with --classic, update needs classic, refresh with --classic works 2402 ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{Classic: true}) 2403 c.Assert(err, IsNil) 2404 c.Assert(ts.Tasks(), Not(HasLen), 0) 2405 snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0]) 2406 c.Assert(err, IsNil) 2407 c.Check(snapsup.Flags.Classic, Equals, true) 2408 2409 // devmode overrides the snapsetup classic flag 2410 ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true}) 2411 c.Assert(err, IsNil) 2412 c.Assert(ts.Tasks(), Not(HasLen), 0) 2413 snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0]) 2414 c.Assert(err, IsNil) 2415 c.Check(snapsup.Flags.Classic, Equals, false) 2416 2417 // jailmode overrides it too (you need to provide both) 2418 ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true}) 2419 c.Assert(err, IsNil) 2420 c.Assert(ts.Tasks(), Not(HasLen), 0) 2421 snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0]) 2422 c.Assert(err, IsNil) 2423 c.Check(snapsup.Flags.Classic, Equals, false) 2424 2425 // jailmode and classic together gets you both 2426 ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true, Classic: true}) 2427 c.Assert(err, IsNil) 2428 c.Assert(ts.Tasks(), Not(HasLen), 0) 2429 snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0]) 2430 c.Assert(err, IsNil) 2431 c.Check(snapsup.Flags.Classic, Equals, true) 2432 2433 // snap installed with --classic, update needs classic, refresh without --classic works 2434 ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{}) 2435 c.Assert(err, IsNil) 2436 c.Assert(ts.Tasks(), Not(HasLen), 0) 2437 snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0]) 2438 c.Assert(err, IsNil) 2439 c.Check(snapsup.Flags.Classic, Equals, true) 2440 2441 chg := s.state.NewChange("refresh", "refresh snap") 2442 chg.AddAll(ts) 2443 2444 s.state.Unlock() 2445 defer s.se.Stop() 2446 s.settle(c) 2447 s.state.Lock() 2448 2449 // verify snap is in classic 2450 var snapst snapstate.SnapState 2451 err = snapstate.Get(s.state, "some-snap", &snapst) 2452 c.Assert(err, IsNil) 2453 c.Check(snapst.Classic, Equals, true) 2454 } 2455 2456 func (s *snapmgrTestSuite) TestUpdateStrictFromClassic(c *C) { 2457 restore := maybeMockClassicSupport(c) 2458 defer restore() 2459 2460 s.state.Lock() 2461 defer s.state.Unlock() 2462 2463 snapstate.Set(s.state, "some-snap-was-classic", &snapstate.SnapState{ 2464 Active: true, 2465 Channel: "channel", 2466 Sequence: []*snap.SideInfo{{RealName: "some-snap-was-classic", SnapID: "some-snap-was-classic-id", Revision: snap.R(7)}}, 2467 Current: snap.R(7), 2468 SnapType: "app", 2469 Flags: snapstate.Flags{Classic: true}, 2470 }) 2471 2472 // snap installed with --classic, update does not need classic, refresh works without --classic 2473 _, err := snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{}) 2474 c.Assert(err, IsNil) 2475 2476 // snap installed with --classic, update does not need classic, refresh works with --classic 2477 _, err = snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{Classic: true}) 2478 c.Assert(err, IsNil) 2479 } 2480 2481 func (s *snapmgrTestSuite) TestUpdateChannelFallback(c *C) { 2482 s.state.Lock() 2483 defer s.state.Unlock() 2484 2485 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2486 Active: true, 2487 Channel: "edge", 2488 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 2489 Current: snap.R(7), 2490 SnapType: "app", 2491 }) 2492 2493 ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{}) 2494 c.Assert(err, IsNil) 2495 2496 var snapsup snapstate.SnapSetup 2497 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 2498 c.Assert(err, IsNil) 2499 2500 c.Check(snapsup.Channel, Equals, "edge") 2501 } 2502 2503 func (s *snapmgrTestSuite) TestUpdateTooEarly(c *C) { 2504 s.state.Lock() 2505 defer s.state.Unlock() 2506 2507 s.state.Set("seeded", nil) 2508 2509 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2510 Active: true, 2511 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 2512 Current: snap.R(7), 2513 SnapType: "app", 2514 }) 2515 2516 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 2517 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 2518 c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`) 2519 } 2520 2521 func (s *snapmgrTestSuite) TestUpdateConflict(c *C) { 2522 s.state.Lock() 2523 defer s.state.Unlock() 2524 2525 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2526 Active: true, 2527 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}}, 2528 Current: snap.R(7), 2529 SnapType: "app", 2530 }) 2531 2532 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 2533 c.Assert(err, IsNil) 2534 // need a change to make the tasks visible 2535 s.state.NewChange("refresh", "...").AddAll(ts) 2536 2537 _, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 2538 c.Assert(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`) 2539 } 2540 2541 func (s *snapmgrTestSuite) TestRemoveTasks(c *C) { 2542 s.state.Lock() 2543 defer s.state.Unlock() 2544 2545 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 2546 Active: true, 2547 Sequence: []*snap.SideInfo{ 2548 {RealName: "foo", Revision: snap.R(11)}, 2549 }, 2550 Current: snap.R(11), 2551 SnapType: "app", 2552 }) 2553 2554 ts, err := snapstate.Remove(s.state, "foo", snap.R(0), nil) 2555 c.Assert(err, IsNil) 2556 2557 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 2558 verifyRemoveTasks(c, ts) 2559 } 2560 2561 func (s *snapmgrTestSuite) TestRemoveTasksAutoSnapshotDisabled(c *C) { 2562 snapstate.AutomaticSnapshot = func(st *state.State, instanceName string) (ts *state.TaskSet, err error) { 2563 return nil, snapstate.ErrNothingToDo 2564 } 2565 2566 s.state.Lock() 2567 defer s.state.Unlock() 2568 2569 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 2570 Active: true, 2571 Sequence: []*snap.SideInfo{ 2572 {RealName: "foo", Revision: snap.R(11)}, 2573 }, 2574 Current: snap.R(11), 2575 SnapType: "app", 2576 }) 2577 2578 ts, err := snapstate.Remove(s.state, "foo", snap.R(0), nil) 2579 c.Assert(err, IsNil) 2580 2581 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 2582 "stop-snap-services", 2583 "run-hook[remove]", 2584 "auto-disconnect", 2585 "remove-aliases", 2586 "unlink-snap", 2587 "remove-profiles", 2588 "clear-snap", 2589 "discard-snap", 2590 }) 2591 } 2592 2593 func (s *snapmgrTestSuite) TestRemoveTasksAutoSnapshotDisabledByPurgeFlag(c *C) { 2594 s.state.Lock() 2595 defer s.state.Unlock() 2596 2597 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 2598 Active: true, 2599 Sequence: []*snap.SideInfo{ 2600 {RealName: "foo", Revision: snap.R(11)}, 2601 }, 2602 Current: snap.R(11), 2603 SnapType: "app", 2604 }) 2605 2606 ts, err := snapstate.Remove(s.state, "foo", snap.R(0), &snapstate.RemoveFlags{Purge: true}) 2607 c.Assert(err, IsNil) 2608 2609 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 2610 "stop-snap-services", 2611 "run-hook[remove]", 2612 "auto-disconnect", 2613 "remove-aliases", 2614 "unlink-snap", 2615 "remove-profiles", 2616 "clear-snap", 2617 "discard-snap", 2618 }) 2619 } 2620 2621 func (s *snapmgrTestSuite) TestRemoveHookNotExecutedIfNotLastRevison(c *C) { 2622 s.state.Lock() 2623 defer s.state.Unlock() 2624 2625 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 2626 Active: true, 2627 Sequence: []*snap.SideInfo{ 2628 {RealName: "foo", Revision: snap.R(11)}, 2629 {RealName: "foo", Revision: snap.R(12)}, 2630 }, 2631 Current: snap.R(12), 2632 }) 2633 2634 ts, err := snapstate.Remove(s.state, "foo", snap.R(11), nil) 2635 c.Assert(err, IsNil) 2636 2637 runHooks := tasksWithKind(ts, "run-hook") 2638 // no 'remove' hook task 2639 c.Assert(runHooks, HasLen, 0) 2640 } 2641 2642 func (s *snapmgrTestSuite) TestRemoveConflict(c *C) { 2643 s.state.Lock() 2644 defer s.state.Unlock() 2645 2646 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2647 Active: true, 2648 Sequence: []*snap.SideInfo{{RealName: "some-snap", Revision: snap.R(11)}}, 2649 Current: snap.R(11), 2650 }) 2651 2652 ts, err := snapstate.Remove(s.state, "some-snap", snap.R(0), nil) 2653 c.Assert(err, IsNil) 2654 // need a change to make the tasks visible 2655 s.state.NewChange("remove", "...").AddAll(ts) 2656 2657 _, err = snapstate.Remove(s.state, "some-snap", snap.R(0), nil) 2658 c.Assert(err, ErrorMatches, `snap "some-snap" has "remove" change in progress`) 2659 } 2660 2661 func (s *snapmgrTestSuite) TestInstallRunThrough(c *C) { 2662 s.state.Lock() 2663 defer s.state.Unlock() 2664 2665 // we start without the auxiliary store info 2666 c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FileAbsent) 2667 2668 chg := s.state.NewChange("install", "install a snap") 2669 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 2670 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2671 c.Assert(err, IsNil) 2672 chg.AddAll(ts) 2673 2674 s.state.Unlock() 2675 defer s.se.Stop() 2676 s.settle(c) 2677 s.state.Lock() 2678 2679 // ensure all our tasks ran 2680 c.Assert(chg.Err(), IsNil) 2681 c.Assert(chg.IsReady(), Equals, true) 2682 c.Check(snapstate.Installing(s.state), Equals, false) 2683 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 2684 macaroon: s.user.StoreMacaroon, 2685 name: "some-snap", 2686 target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 2687 }}) 2688 c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys)) 2689 expected := fakeOps{ 2690 { 2691 op: "storesvc-snap-action", 2692 userID: 1, 2693 }, 2694 { 2695 op: "storesvc-snap-action:action", 2696 action: store.SnapAction{ 2697 Action: "install", 2698 InstanceName: "some-snap", 2699 Channel: "some-channel", 2700 }, 2701 revno: snap.R(11), 2702 userID: 1, 2703 }, 2704 { 2705 op: "storesvc-download", 2706 name: "some-snap", 2707 }, 2708 { 2709 op: "validate-snap:Doing", 2710 name: "some-snap", 2711 revno: snap.R(11), 2712 }, 2713 { 2714 op: "current", 2715 old: "<no-current>", 2716 }, 2717 { 2718 op: "open-snap-file", 2719 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 2720 sinfo: snap.SideInfo{ 2721 RealName: "some-snap", 2722 SnapID: "some-snap-id", 2723 Channel: "some-channel", 2724 Revision: snap.R(11), 2725 }, 2726 }, 2727 { 2728 op: "setup-snap", 2729 name: "some-snap", 2730 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 2731 revno: snap.R(11), 2732 }, 2733 { 2734 op: "copy-data", 2735 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 2736 old: "<no-old>", 2737 }, 2738 { 2739 op: "setup-profiles:Doing", 2740 name: "some-snap", 2741 revno: snap.R(11), 2742 }, 2743 { 2744 op: "candidate", 2745 sinfo: snap.SideInfo{ 2746 RealName: "some-snap", 2747 SnapID: "some-snap-id", 2748 Channel: "some-channel", 2749 Revision: snap.R(11), 2750 }, 2751 }, 2752 { 2753 op: "link-snap", 2754 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 2755 }, 2756 { 2757 op: "auto-connect:Doing", 2758 name: "some-snap", 2759 revno: snap.R(11), 2760 }, 2761 { 2762 op: "update-aliases", 2763 }, 2764 { 2765 op: "cleanup-trash", 2766 name: "some-snap", 2767 revno: snap.R(11), 2768 }, 2769 } 2770 // start with an easier-to-read error if this fails: 2771 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2772 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 2773 2774 // check progress 2775 ta := ts.Tasks() 2776 task := ta[1] 2777 _, cur, total := task.Progress() 2778 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 2779 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 2780 c.Check(task.Summary(), Equals, `Download snap "some-snap" (11) from channel "some-channel"`) 2781 2782 // check link/start snap summary 2783 linkTask := ta[len(ta)-8] 2784 c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (11) available to the system`) 2785 startTask := ta[len(ta)-3] 2786 c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (11) services`) 2787 2788 // verify snap-setup in the task state 2789 var snapsup snapstate.SnapSetup 2790 err = task.Get("snap-setup", &snapsup) 2791 c.Assert(err, IsNil) 2792 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 2793 Channel: "some-channel", 2794 UserID: s.user.ID, 2795 SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 2796 DownloadInfo: &snap.DownloadInfo{ 2797 DownloadURL: "https://some-server.com/some/path.snap", 2798 }, 2799 SideInfo: snapsup.SideInfo, 2800 Type: snap.TypeApp, 2801 PlugsOnly: true, 2802 }) 2803 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 2804 RealName: "some-snap", 2805 Channel: "some-channel", 2806 Revision: snap.R(11), 2807 SnapID: "some-snap-id", 2808 }) 2809 2810 // verify snaps in the system state 2811 var snaps map[string]*snapstate.SnapState 2812 err = s.state.Get("snaps", &snaps) 2813 c.Assert(err, IsNil) 2814 2815 snapst := snaps["some-snap"] 2816 c.Assert(snapst, NotNil) 2817 c.Assert(snapst.Active, Equals, true) 2818 c.Assert(snapst.Channel, Equals, "some-channel") 2819 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 2820 RealName: "some-snap", 2821 SnapID: "some-snap-id", 2822 Channel: "some-channel", 2823 Revision: snap.R(11), 2824 }) 2825 c.Assert(snapst.Required, Equals, false) 2826 2827 // we end with the auxiliary store info 2828 c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FilePresent) 2829 } 2830 2831 func (s *snapmgrTestSuite) TestParallelInstanceInstallRunThrough(c *C) { 2832 s.state.Lock() 2833 defer s.state.Unlock() 2834 2835 tr := config.NewTransaction(s.state) 2836 tr.Set("core", "experimental.parallel-instances", true) 2837 tr.Commit() 2838 2839 chg := s.state.NewChange("install", "install a snap") 2840 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 2841 ts, err := snapstate.Install(context.Background(), s.state, "some-snap_instance", opts, s.user.ID, snapstate.Flags{}) 2842 c.Assert(err, IsNil) 2843 chg.AddAll(ts) 2844 2845 s.state.Unlock() 2846 s.settle(c) 2847 s.state.Lock() 2848 2849 // ensure all our tasks ran 2850 c.Assert(chg.Err(), IsNil) 2851 c.Assert(chg.IsReady(), Equals, true) 2852 c.Check(snapstate.Installing(s.state), Equals, false) 2853 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 2854 macaroon: s.user.StoreMacaroon, 2855 name: "some-snap", 2856 target: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"), 2857 }}) 2858 c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys)) 2859 expected := fakeOps{ 2860 { 2861 op: "storesvc-snap-action", 2862 userID: 1, 2863 }, 2864 { 2865 op: "storesvc-snap-action:action", 2866 action: store.SnapAction{ 2867 Action: "install", 2868 InstanceName: "some-snap_instance", 2869 Channel: "some-channel", 2870 }, 2871 revno: snap.R(11), 2872 userID: 1, 2873 }, 2874 { 2875 op: "storesvc-download", 2876 name: "some-snap", 2877 }, 2878 { 2879 op: "validate-snap:Doing", 2880 name: "some-snap_instance", 2881 revno: snap.R(11), 2882 }, 2883 { 2884 op: "current", 2885 old: "<no-current>", 2886 }, 2887 { 2888 op: "open-snap-file", 2889 path: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"), 2890 sinfo: snap.SideInfo{ 2891 RealName: "some-snap", 2892 SnapID: "some-snap-id", 2893 Channel: "some-channel", 2894 Revision: snap.R(11), 2895 }, 2896 }, 2897 { 2898 op: "setup-snap", 2899 name: "some-snap_instance", 2900 path: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"), 2901 revno: snap.R(11), 2902 }, 2903 { 2904 op: "copy-data", 2905 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/11"), 2906 old: "<no-old>", 2907 }, 2908 { 2909 op: "setup-profiles:Doing", 2910 name: "some-snap_instance", 2911 revno: snap.R(11), 2912 }, 2913 { 2914 op: "candidate", 2915 sinfo: snap.SideInfo{ 2916 RealName: "some-snap", 2917 SnapID: "some-snap-id", 2918 Channel: "some-channel", 2919 Revision: snap.R(11), 2920 }, 2921 }, 2922 { 2923 op: "link-snap", 2924 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/11"), 2925 }, 2926 { 2927 op: "auto-connect:Doing", 2928 name: "some-snap_instance", 2929 revno: snap.R(11), 2930 }, 2931 { 2932 op: "update-aliases", 2933 }, 2934 { 2935 op: "cleanup-trash", 2936 name: "some-snap_instance", 2937 revno: snap.R(11), 2938 }, 2939 } 2940 // start with an easier-to-read error if this fails: 2941 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2942 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 2943 2944 // check progress 2945 ta := ts.Tasks() 2946 task := ta[1] 2947 _, cur, total := task.Progress() 2948 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 2949 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 2950 c.Check(task.Summary(), Equals, `Download snap "some-snap_instance" (11) from channel "some-channel"`) 2951 2952 // check link/start snap summary 2953 linkTask := ta[len(ta)-8] 2954 c.Check(linkTask.Summary(), Equals, `Make snap "some-snap_instance" (11) available to the system`) 2955 startTask := ta[len(ta)-3] 2956 c.Check(startTask.Summary(), Equals, `Start snap "some-snap_instance" (11) services`) 2957 2958 // verify snap-setup in the task state 2959 var snapsup snapstate.SnapSetup 2960 err = task.Get("snap-setup", &snapsup) 2961 c.Assert(err, IsNil) 2962 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 2963 Channel: "some-channel", 2964 UserID: s.user.ID, 2965 SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"), 2966 DownloadInfo: &snap.DownloadInfo{ 2967 DownloadURL: "https://some-server.com/some/path.snap", 2968 }, 2969 SideInfo: snapsup.SideInfo, 2970 Type: snap.TypeApp, 2971 PlugsOnly: true, 2972 InstanceKey: "instance", 2973 }) 2974 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 2975 RealName: "some-snap", 2976 Channel: "some-channel", 2977 Revision: snap.R(11), 2978 SnapID: "some-snap-id", 2979 }) 2980 2981 // verify snaps in the system state 2982 var snaps map[string]*snapstate.SnapState 2983 err = s.state.Get("snaps", &snaps) 2984 c.Assert(err, IsNil) 2985 2986 snapst := snaps["some-snap_instance"] 2987 c.Assert(snapst, NotNil) 2988 c.Assert(snapst.Active, Equals, true) 2989 c.Assert(snapst.Channel, Equals, "some-channel") 2990 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 2991 RealName: "some-snap", 2992 SnapID: "some-snap-id", 2993 Channel: "some-channel", 2994 Revision: snap.R(11), 2995 }) 2996 c.Assert(snapst.Required, Equals, false) 2997 c.Assert(snapst.InstanceKey, Equals, "instance") 2998 2999 runHooks := tasksWithKind(ts, "run-hook") 3000 c.Assert(taskKinds(runHooks), DeepEquals, []string{"run-hook[install]", "run-hook[configure]", "run-hook[check-health]"}) 3001 for _, hookTask := range runHooks { 3002 c.Assert(hookTask.Kind(), Equals, "run-hook") 3003 var hooksup hookstate.HookSetup 3004 err = hookTask.Get("hook-setup", &hooksup) 3005 c.Assert(err, IsNil) 3006 c.Assert(hooksup.Snap, Equals, "some-snap_instance") 3007 } 3008 } 3009 3010 func (s *snapmgrTestSuite) TestInstallUndoRunThroughJustOneSnap(c *C) { 3011 s.state.Lock() 3012 defer s.state.Unlock() 3013 3014 chg := s.state.NewChange("install", "install a snap") 3015 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 3016 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3017 c.Assert(err, IsNil) 3018 chg.AddAll(ts) 3019 3020 tasks := ts.Tasks() 3021 last := tasks[len(tasks)-1] 3022 // sanity 3023 c.Assert(last.Lanes(), HasLen, 1) 3024 terr := s.state.NewTask("error-trigger", "provoking total undo") 3025 terr.WaitFor(last) 3026 terr.JoinLane(last.Lanes()[0]) 3027 chg.AddTask(terr) 3028 3029 s.state.Unlock() 3030 defer s.se.Stop() 3031 s.settle(c) 3032 s.state.Lock() 3033 3034 // ensure all our tasks ran 3035 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 3036 macaroon: s.user.StoreMacaroon, 3037 name: "some-snap", 3038 target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 3039 }}) 3040 expected := fakeOps{ 3041 { 3042 op: "storesvc-snap-action", 3043 userID: 1, 3044 }, 3045 { 3046 op: "storesvc-snap-action:action", 3047 action: store.SnapAction{ 3048 Action: "install", 3049 InstanceName: "some-snap", 3050 Channel: "some-channel", 3051 }, 3052 revno: snap.R(11), 3053 userID: 1, 3054 }, 3055 { 3056 op: "storesvc-download", 3057 name: "some-snap", 3058 }, 3059 { 3060 op: "validate-snap:Doing", 3061 name: "some-snap", 3062 revno: snap.R(11), 3063 }, 3064 { 3065 op: "current", 3066 old: "<no-current>", 3067 }, 3068 { 3069 op: "open-snap-file", 3070 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 3071 sinfo: snap.SideInfo{ 3072 RealName: "some-snap", 3073 SnapID: "some-snap-id", 3074 Channel: "some-channel", 3075 Revision: snap.R(11), 3076 }, 3077 }, 3078 { 3079 op: "setup-snap", 3080 name: "some-snap", 3081 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 3082 revno: snap.R(11), 3083 }, 3084 { 3085 op: "copy-data", 3086 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 3087 old: "<no-old>", 3088 }, 3089 { 3090 op: "setup-profiles:Doing", 3091 name: "some-snap", 3092 revno: snap.R(11), 3093 }, 3094 { 3095 op: "candidate", 3096 sinfo: snap.SideInfo{ 3097 RealName: "some-snap", 3098 SnapID: "some-snap-id", 3099 Channel: "some-channel", 3100 Revision: snap.R(11), 3101 }, 3102 }, 3103 { 3104 op: "link-snap", 3105 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 3106 }, 3107 { 3108 op: "auto-connect:Doing", 3109 name: "some-snap", 3110 revno: snap.R(11), 3111 }, 3112 { 3113 op: "update-aliases", 3114 }, 3115 { 3116 op: "remove-snap-aliases", 3117 name: "some-snap", 3118 }, 3119 { 3120 op: "discard-namespace", 3121 name: "some-snap", 3122 }, 3123 { 3124 op: "unlink-snap", 3125 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 3126 }, 3127 { 3128 op: "setup-profiles:Undoing", 3129 name: "some-snap", 3130 revno: snap.R(11), 3131 }, 3132 { 3133 op: "undo-copy-snap-data", 3134 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 3135 old: "<no-old>", 3136 }, 3137 { 3138 op: "remove-snap-data-dir", 3139 name: "some-snap", 3140 path: filepath.Join(dirs.SnapDataDir, "some-snap"), 3141 }, 3142 { 3143 op: "undo-setup-snap", 3144 name: "some-snap", 3145 stype: "app", 3146 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 3147 }, 3148 { 3149 op: "remove-snap-dir", 3150 name: "some-snap", 3151 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 3152 }, 3153 } 3154 // start with an easier-to-read error if this fails: 3155 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 3156 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 3157 } 3158 3159 func (s *snapmgrTestSuite) TestInstallWithCohortRunThrough(c *C) { 3160 s.state.Lock() 3161 defer s.state.Unlock() 3162 3163 chg := s.state.NewChange("install", "install a snap") 3164 opts := &snapstate.RevisionOptions{Channel: "some-channel", CohortKey: "scurries"} 3165 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3166 c.Assert(err, IsNil) 3167 chg.AddAll(ts) 3168 3169 s.state.Unlock() 3170 defer s.se.Stop() 3171 s.settle(c) 3172 s.state.Lock() 3173 3174 // ensure all our tasks ran 3175 c.Assert(chg.Err(), IsNil) 3176 c.Assert(chg.IsReady(), Equals, true) 3177 c.Check(snapstate.Installing(s.state), Equals, false) 3178 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 3179 macaroon: s.user.StoreMacaroon, 3180 name: "some-snap", 3181 target: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"), 3182 }}) 3183 expected := fakeOps{ 3184 { 3185 op: "storesvc-snap-action", 3186 userID: 1, 3187 }, 3188 { 3189 op: "storesvc-snap-action:action", 3190 action: store.SnapAction{ 3191 Action: "install", 3192 InstanceName: "some-snap", 3193 CohortKey: "scurries", 3194 Channel: "some-channel", 3195 }, 3196 revno: snap.R(666), 3197 userID: 1, 3198 }, 3199 { 3200 op: "storesvc-download", 3201 name: "some-snap", 3202 }, 3203 { 3204 op: "validate-snap:Doing", 3205 name: "some-snap", 3206 revno: snap.R(666), 3207 }, 3208 { 3209 op: "current", 3210 old: "<no-current>", 3211 }, 3212 { 3213 op: "open-snap-file", 3214 path: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"), 3215 sinfo: snap.SideInfo{ 3216 RealName: "some-snap", 3217 SnapID: "some-snap-id", 3218 Revision: snap.R(666), 3219 Channel: "some-channel", 3220 }, 3221 }, 3222 { 3223 op: "setup-snap", 3224 name: "some-snap", 3225 path: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"), 3226 revno: snap.R(666), 3227 }, 3228 { 3229 op: "copy-data", 3230 path: filepath.Join(dirs.SnapMountDir, "some-snap/666"), 3231 old: "<no-old>", 3232 }, 3233 { 3234 op: "setup-profiles:Doing", 3235 name: "some-snap", 3236 revno: snap.R(666), 3237 }, 3238 { 3239 op: "candidate", 3240 sinfo: snap.SideInfo{ 3241 RealName: "some-snap", 3242 SnapID: "some-snap-id", 3243 Revision: snap.R(666), 3244 Channel: "some-channel", 3245 }, 3246 }, 3247 { 3248 op: "link-snap", 3249 path: filepath.Join(dirs.SnapMountDir, "some-snap/666"), 3250 }, 3251 { 3252 op: "auto-connect:Doing", 3253 name: "some-snap", 3254 revno: snap.R(666), 3255 }, 3256 { 3257 op: "update-aliases", 3258 }, 3259 { 3260 op: "cleanup-trash", 3261 name: "some-snap", 3262 revno: snap.R(666), 3263 }, 3264 } 3265 // start with an easier-to-read error if this fails: 3266 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 3267 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 3268 3269 // check progress 3270 ta := ts.Tasks() 3271 task := ta[1] 3272 _, cur, total := task.Progress() 3273 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 3274 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 3275 c.Check(task.Summary(), Equals, `Download snap "some-snap" (666) from channel "some-channel"`) 3276 3277 // check link/start snap summary 3278 linkTask := ta[len(ta)-8] 3279 c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (666) available to the system`) 3280 startTask := ta[len(ta)-3] 3281 c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (666) services`) 3282 3283 // verify snap-setup in the task state 3284 var snapsup snapstate.SnapSetup 3285 err = task.Get("snap-setup", &snapsup) 3286 c.Assert(err, IsNil) 3287 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 3288 Channel: "some-channel", 3289 UserID: s.user.ID, 3290 SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"), 3291 DownloadInfo: &snap.DownloadInfo{ 3292 DownloadURL: "https://some-server.com/some/path.snap", 3293 }, 3294 SideInfo: snapsup.SideInfo, 3295 Type: snap.TypeApp, 3296 PlugsOnly: true, 3297 CohortKey: "scurries", 3298 }) 3299 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 3300 RealName: "some-snap", 3301 Revision: snap.R(666), 3302 SnapID: "some-snap-id", 3303 Channel: "some-channel", 3304 }) 3305 3306 // verify snaps in the system state 3307 var snaps map[string]*snapstate.SnapState 3308 err = s.state.Get("snaps", &snaps) 3309 c.Assert(err, IsNil) 3310 3311 snapst := snaps["some-snap"] 3312 c.Assert(snapst, NotNil) 3313 c.Assert(snapst.Active, Equals, true) 3314 c.Assert(snapst.Channel, Equals, "some-channel") 3315 c.Assert(snapst.CohortKey, Equals, "scurries") 3316 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 3317 RealName: "some-snap", 3318 SnapID: "some-snap-id", 3319 Revision: snap.R(666), 3320 Channel: "some-channel", 3321 }) 3322 c.Assert(snapst.Required, Equals, false) 3323 } 3324 3325 func (s *snapmgrTestSuite) TestInstallWithRevisionRunThrough(c *C) { 3326 s.state.Lock() 3327 defer s.state.Unlock() 3328 3329 chg := s.state.NewChange("install", "install a snap") 3330 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 3331 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3332 c.Assert(err, IsNil) 3333 chg.AddAll(ts) 3334 3335 s.state.Unlock() 3336 defer s.se.Stop() 3337 s.settle(c) 3338 s.state.Lock() 3339 3340 // ensure all our tasks ran 3341 c.Assert(chg.Err(), IsNil) 3342 c.Assert(chg.IsReady(), Equals, true) 3343 c.Check(snapstate.Installing(s.state), Equals, false) 3344 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 3345 macaroon: s.user.StoreMacaroon, 3346 name: "some-snap", 3347 target: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 3348 }}) 3349 expected := fakeOps{ 3350 { 3351 op: "storesvc-snap-action", 3352 userID: 1, 3353 }, 3354 { 3355 op: "storesvc-snap-action:action", 3356 action: store.SnapAction{ 3357 Action: "install", 3358 InstanceName: "some-snap", 3359 Revision: snap.R(42), 3360 }, 3361 revno: snap.R(42), 3362 userID: 1, 3363 }, 3364 { 3365 op: "storesvc-download", 3366 name: "some-snap", 3367 }, 3368 { 3369 op: "validate-snap:Doing", 3370 name: "some-snap", 3371 revno: snap.R(42), 3372 }, 3373 { 3374 op: "current", 3375 old: "<no-current>", 3376 }, 3377 { 3378 op: "open-snap-file", 3379 path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 3380 sinfo: snap.SideInfo{ 3381 RealName: "some-snap", 3382 SnapID: "some-snap-id", 3383 Revision: snap.R(42), 3384 }, 3385 }, 3386 { 3387 op: "setup-snap", 3388 name: "some-snap", 3389 path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 3390 revno: snap.R(42), 3391 }, 3392 { 3393 op: "copy-data", 3394 path: filepath.Join(dirs.SnapMountDir, "some-snap/42"), 3395 old: "<no-old>", 3396 }, 3397 { 3398 op: "setup-profiles:Doing", 3399 name: "some-snap", 3400 revno: snap.R(42), 3401 }, 3402 { 3403 op: "candidate", 3404 sinfo: snap.SideInfo{ 3405 RealName: "some-snap", 3406 SnapID: "some-snap-id", 3407 Revision: snap.R(42), 3408 }, 3409 }, 3410 { 3411 op: "link-snap", 3412 path: filepath.Join(dirs.SnapMountDir, "some-snap/42"), 3413 }, 3414 { 3415 op: "auto-connect:Doing", 3416 name: "some-snap", 3417 revno: snap.R(42), 3418 }, 3419 { 3420 op: "update-aliases", 3421 }, 3422 { 3423 op: "cleanup-trash", 3424 name: "some-snap", 3425 revno: snap.R(42), 3426 }, 3427 } 3428 // start with an easier-to-read error if this fails: 3429 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 3430 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 3431 3432 // check progress 3433 ta := ts.Tasks() 3434 task := ta[1] 3435 _, cur, total := task.Progress() 3436 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 3437 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 3438 c.Check(task.Summary(), Equals, `Download snap "some-snap" (42) from channel "some-channel"`) 3439 3440 // check link/start snap summary 3441 linkTask := ta[len(ta)-8] 3442 c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (42) available to the system`) 3443 startTask := ta[len(ta)-3] 3444 c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (42) services`) 3445 3446 // verify snap-setup in the task state 3447 var snapsup snapstate.SnapSetup 3448 err = task.Get("snap-setup", &snapsup) 3449 c.Assert(err, IsNil) 3450 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 3451 Channel: "some-channel", 3452 UserID: s.user.ID, 3453 SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 3454 DownloadInfo: &snap.DownloadInfo{ 3455 DownloadURL: "https://some-server.com/some/path.snap", 3456 }, 3457 SideInfo: snapsup.SideInfo, 3458 Type: snap.TypeApp, 3459 PlugsOnly: true, 3460 }) 3461 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 3462 RealName: "some-snap", 3463 Revision: snap.R(42), 3464 SnapID: "some-snap-id", 3465 }) 3466 3467 // verify snaps in the system state 3468 var snaps map[string]*snapstate.SnapState 3469 err = s.state.Get("snaps", &snaps) 3470 c.Assert(err, IsNil) 3471 3472 snapst := snaps["some-snap"] 3473 c.Assert(snapst, NotNil) 3474 c.Assert(snapst.Active, Equals, true) 3475 c.Assert(snapst.Channel, Equals, "some-channel") 3476 c.Assert(snapst.CohortKey, Equals, "") 3477 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 3478 RealName: "some-snap", 3479 SnapID: "some-snap-id", 3480 Revision: snap.R(42), 3481 }) 3482 c.Assert(snapst.Required, Equals, false) 3483 } 3484 3485 func (s *snapmgrTestSuite) TestInstallStartOrder(c *C) { 3486 s.state.Lock() 3487 defer s.state.Unlock() 3488 3489 chg := s.state.NewChange("install", "install a snap") 3490 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 3491 ts, err := snapstate.Install(context.Background(), s.state, "services-snap", opts, s.user.ID, snapstate.Flags{}) 3492 c.Assert(err, IsNil) 3493 chg.AddAll(ts) 3494 3495 s.state.Unlock() 3496 defer s.se.Stop() 3497 s.settle(c) 3498 s.state.Lock() 3499 3500 // ensure all our tasks ran 3501 c.Assert(chg.Err(), IsNil) 3502 c.Assert(chg.IsReady(), Equals, true) 3503 c.Check(snapstate.Installing(s.state), Equals, false) 3504 op := s.fakeBackend.ops.First("start-snap-services") 3505 c.Assert(op, NotNil) 3506 c.Assert(op, DeepEquals, &fakeOp{ 3507 op: "start-snap-services", 3508 path: filepath.Join(dirs.SnapMountDir, "services-snap/11"), 3509 // ordered to preserve after/before relation 3510 services: []string{"svc1", "svc3", "svc2"}, 3511 }) 3512 } 3513 3514 func (s *snapmgrTestSuite) TestInstalling(c *C) { 3515 s.state.Lock() 3516 defer s.state.Unlock() 3517 3518 c.Check(snapstate.Installing(s.state), Equals, false) 3519 3520 chg := s.state.NewChange("install", "install a snap") 3521 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 3522 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}) 3523 c.Assert(err, IsNil) 3524 chg.AddAll(ts) 3525 3526 c.Check(snapstate.Installing(s.state), Equals, true) 3527 } 3528 3529 func (s *snapmgrTestSuite) TestUpdateAmendRunThrough(c *C) { 3530 si := snap.SideInfo{ 3531 RealName: "some-snap", 3532 Revision: snap.R(-42), 3533 } 3534 snaptest.MockSnap(c, `name: some-snap`, &si) 3535 3536 s.state.Lock() 3537 defer s.state.Unlock() 3538 3539 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 3540 Active: true, 3541 Sequence: []*snap.SideInfo{&si}, 3542 Current: si.Revision, 3543 SnapType: "app", 3544 Channel: "stable", 3545 }) 3546 3547 chg := s.state.NewChange("refresh", "refresh a snap") 3548 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true}) 3549 c.Assert(err, IsNil) 3550 chg.AddAll(ts) 3551 3552 s.state.Unlock() 3553 defer s.se.Stop() 3554 s.settle(c) 3555 s.state.Lock() 3556 3557 // ensure all our tasks ran 3558 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 3559 macaroon: s.user.StoreMacaroon, 3560 name: "some-snap", 3561 target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 3562 }}) 3563 c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys)) 3564 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, []string{ 3565 "storesvc-snap-action", 3566 "storesvc-snap-action:action", 3567 "storesvc-download", 3568 "validate-snap:Doing", 3569 "current", 3570 "open-snap-file", 3571 "setup-snap", 3572 "remove-snap-aliases", 3573 "unlink-snap", 3574 "copy-data", 3575 "setup-profiles:Doing", 3576 "candidate", 3577 "link-snap", 3578 "auto-connect:Doing", 3579 "update-aliases", 3580 "cleanup-trash", 3581 }) 3582 // just check the interesting op 3583 c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{ 3584 op: "storesvc-snap-action:action", 3585 action: store.SnapAction{ 3586 Action: "install", // we asked for an Update, but an amend is actually an Install 3587 InstanceName: "some-snap", 3588 Channel: "some-channel", 3589 Epoch: snap.E("1*"), // in amend, epoch in the action is not nil! 3590 Flags: store.SnapActionEnforceValidation, 3591 }, 3592 revno: snap.R(11), 3593 userID: 1, 3594 }) 3595 3596 task := ts.Tasks()[1] 3597 // verify snapSetup info 3598 var snapsup snapstate.SnapSetup 3599 err = task.Get("snap-setup", &snapsup) 3600 c.Assert(err, IsNil) 3601 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 3602 Channel: "some-channel", 3603 UserID: s.user.ID, 3604 3605 SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 3606 DownloadInfo: &snap.DownloadInfo{ 3607 DownloadURL: "https://some-server.com/some/path.snap", 3608 }, 3609 SideInfo: snapsup.SideInfo, 3610 Type: snap.TypeApp, 3611 PlugsOnly: true, 3612 Flags: snapstate.Flags{Amend: true}, 3613 }) 3614 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 3615 RealName: "some-snap", 3616 Revision: snap.R(11), 3617 Channel: "some-channel", 3618 SnapID: "some-snap-id", 3619 }) 3620 3621 // verify services stop reason 3622 verifyStopReason(c, ts, "refresh") 3623 3624 // check post-refresh hook 3625 task = ts.Tasks()[14] 3626 c.Assert(task.Kind(), Equals, "run-hook") 3627 c.Assert(task.Summary(), Matches, `Run post-refresh hook of "some-snap" snap if present`) 3628 3629 // verify snaps in the system state 3630 var snapst snapstate.SnapState 3631 err = snapstate.Get(s.state, "some-snap", &snapst) 3632 c.Assert(err, IsNil) 3633 3634 c.Assert(snapst.Active, Equals, true) 3635 c.Assert(snapst.Sequence, HasLen, 2) 3636 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 3637 RealName: "some-snap", 3638 Channel: "", 3639 Revision: snap.R(-42), 3640 }) 3641 c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{ 3642 RealName: "some-snap", 3643 Channel: "some-channel", 3644 SnapID: "some-snap-id", 3645 Revision: snap.R(11), 3646 }) 3647 } 3648 3649 func (s *snapmgrTestSuite) TestUpdateRunThrough(c *C) { 3650 // we start without the auxiliary store info (or with an older one) 3651 c.Check(snapstate.AuxStoreInfoFilename("services-snap-id"), testutil.FileAbsent) 3652 3653 // use services-snap here to make sure services would be stopped/started appropriately 3654 si := snap.SideInfo{ 3655 RealName: "services-snap", 3656 Revision: snap.R(7), 3657 SnapID: "services-snap-id", 3658 } 3659 snaptest.MockSnap(c, `name: services-snap`, &si) 3660 fi, err := os.Stat(snap.MountFile("services-snap", si.Revision)) 3661 c.Assert(err, IsNil) 3662 refreshedDate := fi.ModTime() 3663 // look at disk 3664 r := snapstate.MockRevisionDate(nil) 3665 defer r() 3666 3667 s.state.Lock() 3668 defer s.state.Unlock() 3669 3670 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 3671 Active: true, 3672 Sequence: []*snap.SideInfo{&si}, 3673 Current: si.Revision, 3674 SnapType: "app", 3675 Channel: "stable", 3676 CohortKey: "embattled", 3677 }) 3678 3679 chg := s.state.NewChange("refresh", "refresh a snap") 3680 ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{ 3681 Channel: "some-channel", 3682 CohortKey: "some-cohort", 3683 }, s.user.ID, snapstate.Flags{}) 3684 c.Assert(err, IsNil) 3685 chg.AddAll(ts) 3686 3687 s.state.Unlock() 3688 defer s.se.Stop() 3689 s.settle(c) 3690 s.state.Lock() 3691 3692 expected := fakeOps{ 3693 { 3694 op: "storesvc-snap-action", 3695 curSnaps: []store.CurrentSnap{{ 3696 InstanceName: "services-snap", 3697 SnapID: "services-snap-id", 3698 Revision: snap.R(7), 3699 TrackingChannel: "stable", 3700 RefreshedDate: refreshedDate, 3701 Epoch: snap.E("0"), 3702 CohortKey: "embattled", 3703 }}, 3704 userID: 1, 3705 }, 3706 { 3707 op: "storesvc-snap-action:action", 3708 action: store.SnapAction{ 3709 Action: "refresh", 3710 InstanceName: "services-snap", 3711 SnapID: "services-snap-id", 3712 Channel: "some-channel", 3713 CohortKey: "some-cohort", 3714 Flags: store.SnapActionEnforceValidation, 3715 }, 3716 revno: snap.R(11), 3717 userID: 1, 3718 }, 3719 { 3720 op: "storesvc-download", 3721 name: "services-snap", 3722 }, 3723 { 3724 op: "validate-snap:Doing", 3725 name: "services-snap", 3726 revno: snap.R(11), 3727 }, 3728 { 3729 op: "current", 3730 old: filepath.Join(dirs.SnapMountDir, "services-snap/7"), 3731 }, 3732 { 3733 op: "open-snap-file", 3734 path: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"), 3735 sinfo: snap.SideInfo{ 3736 RealName: "services-snap", 3737 SnapID: "services-snap-id", 3738 Channel: "some-channel", 3739 Revision: snap.R(11), 3740 }, 3741 }, 3742 { 3743 op: "setup-snap", 3744 name: "services-snap", 3745 path: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"), 3746 revno: snap.R(11), 3747 }, 3748 { 3749 op: "stop-snap-services:refresh", 3750 path: filepath.Join(dirs.SnapMountDir, "services-snap/7"), 3751 }, 3752 { 3753 op: "remove-snap-aliases", 3754 name: "services-snap", 3755 }, 3756 { 3757 op: "unlink-snap", 3758 path: filepath.Join(dirs.SnapMountDir, "services-snap/7"), 3759 }, 3760 { 3761 op: "copy-data", 3762 path: filepath.Join(dirs.SnapMountDir, "services-snap/11"), 3763 old: filepath.Join(dirs.SnapMountDir, "services-snap/7"), 3764 }, 3765 { 3766 op: "setup-profiles:Doing", 3767 name: "services-snap", 3768 revno: snap.R(11), 3769 }, 3770 { 3771 op: "candidate", 3772 sinfo: snap.SideInfo{ 3773 RealName: "services-snap", 3774 SnapID: "services-snap-id", 3775 Channel: "some-channel", 3776 Revision: snap.R(11), 3777 }, 3778 }, 3779 { 3780 op: "link-snap", 3781 path: filepath.Join(dirs.SnapMountDir, "services-snap/11"), 3782 }, 3783 { 3784 op: "auto-connect:Doing", 3785 name: "services-snap", 3786 revno: snap.R(11), 3787 }, 3788 { 3789 op: "update-aliases", 3790 }, 3791 { 3792 op: "start-snap-services", 3793 path: filepath.Join(dirs.SnapMountDir, "services-snap/11"), 3794 services: []string{"svc1", "svc3", "svc2"}, 3795 }, 3796 { 3797 op: "cleanup-trash", 3798 name: "services-snap", 3799 revno: snap.R(11), 3800 }, 3801 } 3802 3803 // ensure all our tasks ran 3804 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 3805 macaroon: s.user.StoreMacaroon, 3806 name: "services-snap", 3807 target: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"), 3808 }}) 3809 c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys)) 3810 // start with an easier-to-read error if this fails: 3811 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 3812 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 3813 3814 // check progress 3815 task := ts.Tasks()[1] 3816 _, cur, total := task.Progress() 3817 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 3818 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 3819 3820 // verify snapSetup info 3821 var snapsup snapstate.SnapSetup 3822 err = task.Get("snap-setup", &snapsup) 3823 c.Assert(err, IsNil) 3824 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 3825 Channel: "some-channel", 3826 CohortKey: "some-cohort", 3827 UserID: s.user.ID, 3828 3829 SnapPath: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"), 3830 DownloadInfo: &snap.DownloadInfo{ 3831 DownloadURL: "https://some-server.com/some/path.snap", 3832 }, 3833 SideInfo: snapsup.SideInfo, 3834 Type: snap.TypeApp, 3835 PlugsOnly: true, 3836 }) 3837 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 3838 RealName: "services-snap", 3839 Revision: snap.R(11), 3840 Channel: "some-channel", 3841 SnapID: "services-snap-id", 3842 }) 3843 3844 // verify services stop reason 3845 verifyStopReason(c, ts, "refresh") 3846 3847 // check post-refresh hook 3848 task = ts.Tasks()[14] 3849 c.Assert(task.Kind(), Equals, "run-hook") 3850 c.Assert(task.Summary(), Matches, `Run post-refresh hook of "services-snap" snap if present`) 3851 3852 // verify snaps in the system state 3853 var snapst snapstate.SnapState 3854 err = snapstate.Get(s.state, "services-snap", &snapst) 3855 c.Assert(err, IsNil) 3856 3857 c.Assert(snapst.Active, Equals, true) 3858 c.Assert(snapst.Sequence, HasLen, 2) 3859 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 3860 RealName: "services-snap", 3861 SnapID: "services-snap-id", 3862 Channel: "", 3863 Revision: snap.R(7), 3864 }) 3865 c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{ 3866 RealName: "services-snap", 3867 Channel: "some-channel", 3868 SnapID: "services-snap-id", 3869 Revision: snap.R(11), 3870 }) 3871 c.Check(snapst.CohortKey, Equals, "some-cohort") 3872 3873 // we end up with the auxiliary store info 3874 c.Check(snapstate.AuxStoreInfoFilename("services-snap-id"), testutil.FilePresent) 3875 } 3876 3877 func (s *snapmgrTestSuite) TestParallelInstanceUpdateRunThrough(c *C) { 3878 // use services-snap here to make sure services would be stopped/started appropriately 3879 si := snap.SideInfo{ 3880 RealName: "services-snap", 3881 Revision: snap.R(7), 3882 SnapID: "services-snap-id", 3883 } 3884 snaptest.MockSnapInstance(c, "services-snap_instance", `name: services-snap`, &si) 3885 fi, err := os.Stat(snap.MountFile("services-snap_instance", si.Revision)) 3886 c.Assert(err, IsNil) 3887 refreshedDate := fi.ModTime() 3888 // look at disk 3889 r := snapstate.MockRevisionDate(nil) 3890 defer r() 3891 3892 s.state.Lock() 3893 defer s.state.Unlock() 3894 3895 tr := config.NewTransaction(s.state) 3896 tr.Set("core", "experimental.parallel-instances", true) 3897 tr.Commit() 3898 3899 snapstate.Set(s.state, "services-snap_instance", &snapstate.SnapState{ 3900 Active: true, 3901 Sequence: []*snap.SideInfo{&si}, 3902 Current: si.Revision, 3903 SnapType: "app", 3904 Channel: "stable", 3905 InstanceKey: "instance", 3906 }) 3907 3908 chg := s.state.NewChange("refresh", "refresh a snap") 3909 ts, err := snapstate.Update(s.state, "services-snap_instance", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 3910 c.Assert(err, IsNil) 3911 chg.AddAll(ts) 3912 3913 s.state.Unlock() 3914 s.settle(c) 3915 s.state.Lock() 3916 3917 expected := fakeOps{ 3918 { 3919 op: "storesvc-snap-action", 3920 curSnaps: []store.CurrentSnap{{ 3921 InstanceName: "services-snap_instance", 3922 SnapID: "services-snap-id", 3923 Revision: snap.R(7), 3924 TrackingChannel: "stable", 3925 RefreshedDate: refreshedDate, 3926 Epoch: snap.E("0"), 3927 }}, 3928 userID: 1, 3929 }, 3930 { 3931 op: "storesvc-snap-action:action", 3932 action: store.SnapAction{ 3933 Action: "refresh", 3934 SnapID: "services-snap-id", 3935 InstanceName: "services-snap_instance", 3936 Channel: "some-channel", 3937 Flags: store.SnapActionEnforceValidation, 3938 }, 3939 revno: snap.R(11), 3940 userID: 1, 3941 }, 3942 { 3943 op: "storesvc-download", 3944 name: "services-snap", 3945 }, 3946 { 3947 op: "validate-snap:Doing", 3948 name: "services-snap_instance", 3949 revno: snap.R(11), 3950 }, 3951 { 3952 op: "current", 3953 old: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"), 3954 }, 3955 { 3956 op: "open-snap-file", 3957 path: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"), 3958 sinfo: snap.SideInfo{ 3959 RealName: "services-snap", 3960 SnapID: "services-snap-id", 3961 Channel: "some-channel", 3962 Revision: snap.R(11), 3963 }, 3964 }, 3965 { 3966 op: "setup-snap", 3967 name: "services-snap_instance", 3968 path: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"), 3969 revno: snap.R(11), 3970 }, 3971 { 3972 op: "stop-snap-services:refresh", 3973 path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"), 3974 }, 3975 { 3976 op: "remove-snap-aliases", 3977 name: "services-snap_instance", 3978 }, 3979 { 3980 op: "unlink-snap", 3981 path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"), 3982 }, 3983 { 3984 op: "copy-data", 3985 path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"), 3986 old: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"), 3987 }, 3988 { 3989 op: "setup-profiles:Doing", 3990 name: "services-snap_instance", 3991 revno: snap.R(11), 3992 }, 3993 { 3994 op: "candidate", 3995 sinfo: snap.SideInfo{ 3996 RealName: "services-snap", 3997 SnapID: "services-snap-id", 3998 Channel: "some-channel", 3999 Revision: snap.R(11), 4000 }, 4001 }, 4002 { 4003 op: "link-snap", 4004 path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"), 4005 }, 4006 { 4007 op: "auto-connect:Doing", 4008 name: "services-snap_instance", 4009 revno: snap.R(11), 4010 }, 4011 { 4012 op: "update-aliases", 4013 }, 4014 { 4015 op: "start-snap-services", 4016 path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"), 4017 services: []string{"svc1", "svc3", "svc2"}, 4018 }, 4019 { 4020 op: "cleanup-trash", 4021 name: "services-snap_instance", 4022 revno: snap.R(11), 4023 }, 4024 } 4025 4026 // ensure all our tasks ran 4027 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 4028 macaroon: s.user.StoreMacaroon, 4029 name: "services-snap", 4030 target: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"), 4031 }}) 4032 c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys)) 4033 // start with an easier-to-read error if this fails: 4034 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 4035 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 4036 4037 // check progress 4038 task := ts.Tasks()[1] 4039 _, cur, total := task.Progress() 4040 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 4041 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 4042 4043 // verify snapSetup info 4044 var snapsup snapstate.SnapSetup 4045 err = task.Get("snap-setup", &snapsup) 4046 c.Assert(err, IsNil) 4047 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 4048 Channel: "some-channel", 4049 UserID: s.user.ID, 4050 4051 SnapPath: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"), 4052 DownloadInfo: &snap.DownloadInfo{ 4053 DownloadURL: "https://some-server.com/some/path.snap", 4054 }, 4055 SideInfo: snapsup.SideInfo, 4056 Type: snap.TypeApp, 4057 PlugsOnly: true, 4058 InstanceKey: "instance", 4059 }) 4060 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 4061 RealName: "services-snap", 4062 Revision: snap.R(11), 4063 Channel: "some-channel", 4064 SnapID: "services-snap-id", 4065 }) 4066 4067 // verify services stop reason 4068 verifyStopReason(c, ts, "refresh") 4069 4070 // check post-refresh hook 4071 task = ts.Tasks()[14] 4072 c.Assert(task.Kind(), Equals, "run-hook") 4073 c.Assert(task.Summary(), Matches, `Run post-refresh hook of "services-snap_instance" snap if present`) 4074 4075 // verify snaps in the system state 4076 var snapst snapstate.SnapState 4077 err = snapstate.Get(s.state, "services-snap_instance", &snapst) 4078 c.Assert(err, IsNil) 4079 4080 c.Assert(snapst.InstanceKey, Equals, "instance") 4081 c.Assert(snapst.Active, Equals, true) 4082 c.Assert(snapst.Sequence, HasLen, 2) 4083 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 4084 RealName: "services-snap", 4085 SnapID: "services-snap-id", 4086 Channel: "", 4087 Revision: snap.R(7), 4088 }) 4089 c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{ 4090 RealName: "services-snap", 4091 Channel: "some-channel", 4092 SnapID: "services-snap-id", 4093 Revision: snap.R(11), 4094 }) 4095 } 4096 4097 func (s *snapmgrTestSuite) TestUpdateWithNewBase(c *C) { 4098 si := &snap.SideInfo{ 4099 RealName: "some-snap", 4100 SnapID: "some-snap-id", 4101 Revision: snap.R(7), 4102 } 4103 snaptest.MockSnap(c, `name: some-snap`, si) 4104 4105 s.state.Lock() 4106 defer s.state.Unlock() 4107 4108 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 4109 Active: true, 4110 Channel: "edge", 4111 Sequence: []*snap.SideInfo{si}, 4112 Current: snap.R(7), 4113 SnapType: "app", 4114 }) 4115 4116 chg := s.state.NewChange("refresh", "refresh a snap") 4117 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-base"}, s.user.ID, snapstate.Flags{}) 4118 c.Assert(err, IsNil) 4119 chg.AddAll(ts) 4120 4121 s.state.Unlock() 4122 defer s.se.Stop() 4123 s.settle(c) 4124 s.state.Lock() 4125 4126 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{ 4127 {macaroon: s.user.StoreMacaroon, name: "some-base", target: filepath.Join(dirs.SnapBlobDir, "some-base_11.snap")}, 4128 {macaroon: s.user.StoreMacaroon, name: "some-snap", target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap")}, 4129 }) 4130 } 4131 4132 func (s *snapmgrTestSuite) TestUpdateWithAlreadyInstalledBase(c *C) { 4133 si := &snap.SideInfo{ 4134 RealName: "some-snap", 4135 SnapID: "some-snap-id", 4136 Revision: snap.R(7), 4137 } 4138 snaptest.MockSnap(c, `name: some-snap`, si) 4139 4140 s.state.Lock() 4141 defer s.state.Unlock() 4142 4143 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 4144 Active: true, 4145 Channel: "edge", 4146 Sequence: []*snap.SideInfo{si}, 4147 Current: snap.R(7), 4148 SnapType: "app", 4149 }) 4150 snapstate.Set(s.state, "some-base", &snapstate.SnapState{ 4151 Active: true, 4152 Channel: "stable", 4153 Sequence: []*snap.SideInfo{{ 4154 RealName: "some-base", 4155 SnapID: "some-base-id", 4156 Revision: snap.R(1), 4157 }}, 4158 Current: snap.R(1), 4159 SnapType: "base", 4160 }) 4161 4162 chg := s.state.NewChange("refresh", "refresh a snap") 4163 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-base"}, s.user.ID, snapstate.Flags{}) 4164 c.Assert(err, IsNil) 4165 chg.AddAll(ts) 4166 4167 s.state.Unlock() 4168 defer s.se.Stop() 4169 s.settle(c) 4170 s.state.Lock() 4171 4172 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{ 4173 {macaroon: s.user.StoreMacaroon, name: "some-snap", target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap")}, 4174 }) 4175 } 4176 4177 func (s *snapmgrTestSuite) TestUpdateWithNewDefaultProvider(c *C) { 4178 s.state.Lock() 4179 defer s.state.Unlock() 4180 4181 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 4182 repo := interfaces.NewRepository() 4183 ifacerepo.Replace(s.state, repo) 4184 4185 si := &snap.SideInfo{ 4186 RealName: "snap-content-plug", 4187 SnapID: "snap-content-plug-id", 4188 Revision: snap.R(7), 4189 } 4190 snaptest.MockSnap(c, `name: snap-content-plug`, si) 4191 snapstate.Set(s.state, "snap-content-plug", &snapstate.SnapState{ 4192 Active: true, 4193 Channel: "edge", 4194 Sequence: []*snap.SideInfo{si}, 4195 Current: snap.R(7), 4196 SnapType: "app", 4197 }) 4198 4199 chg := s.state.NewChange("refresh", "refresh a snap") 4200 ts, err := snapstate.Update(s.state, "snap-content-plug", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{}) 4201 c.Assert(err, IsNil) 4202 chg.AddAll(ts) 4203 4204 s.state.Unlock() 4205 defer s.se.Stop() 4206 s.settle(c) 4207 s.state.Lock() 4208 4209 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{ 4210 {macaroon: s.user.StoreMacaroon, name: "snap-content-plug", target: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_11.snap")}, 4211 {macaroon: s.user.StoreMacaroon, name: "snap-content-slot", target: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap")}, 4212 }) 4213 } 4214 4215 func (s *snapmgrTestSuite) TestUpdateWithInstalledDefaultProvider(c *C) { 4216 s.state.Lock() 4217 defer s.state.Unlock() 4218 4219 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 4220 repo := interfaces.NewRepository() 4221 ifacerepo.Replace(s.state, repo) 4222 4223 si := &snap.SideInfo{ 4224 RealName: "snap-content-plug", 4225 SnapID: "snap-content-plug-id", 4226 Revision: snap.R(7), 4227 } 4228 snaptest.MockSnap(c, `name: snap-content-plug`, si) 4229 snapstate.Set(s.state, "snap-content-plug", &snapstate.SnapState{ 4230 Active: true, 4231 Channel: "edge", 4232 Sequence: []*snap.SideInfo{si}, 4233 Current: snap.R(7), 4234 SnapType: "app", 4235 }) 4236 snapstate.Set(s.state, "snap-content-slot", &snapstate.SnapState{ 4237 Active: true, 4238 Channel: "stable", 4239 Sequence: []*snap.SideInfo{{ 4240 RealName: "snap-content-slot", 4241 SnapID: "snap-content-slot-id", 4242 Revision: snap.R(1), 4243 }}, 4244 Current: snap.R(1), 4245 SnapType: "app", 4246 }) 4247 4248 chg := s.state.NewChange("refresh", "refresh a snap") 4249 ts, err := snapstate.Update(s.state, "snap-content-plug", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{}) 4250 c.Assert(err, IsNil) 4251 chg.AddAll(ts) 4252 4253 s.state.Unlock() 4254 defer s.se.Stop() 4255 s.settle(c) 4256 s.state.Lock() 4257 4258 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{ 4259 {macaroon: s.user.StoreMacaroon, name: "snap-content-plug", target: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_11.snap")}, 4260 }) 4261 } 4262 4263 func (s *snapmgrTestSuite) TestUpdateRememberedUserRunThrough(c *C) { 4264 s.state.Lock() 4265 defer s.state.Unlock() 4266 4267 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 4268 Active: true, 4269 Sequence: []*snap.SideInfo{ 4270 {RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"}, 4271 }, 4272 Current: snap.R(5), 4273 SnapType: "app", 4274 UserID: 1, 4275 }) 4276 4277 chg := s.state.NewChange("refresh", "refresh a snap") 4278 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, 0, snapstate.Flags{}) 4279 c.Assert(err, IsNil) 4280 chg.AddAll(ts) 4281 4282 s.state.Unlock() 4283 defer s.se.Stop() 4284 s.settle(c) 4285 s.state.Lock() 4286 4287 c.Assert(chg.Status(), Equals, state.DoneStatus) 4288 c.Assert(chg.Err(), IsNil) 4289 4290 for _, op := range s.fakeBackend.ops { 4291 switch op.op { 4292 case "storesvc-snap-action": 4293 c.Check(op.userID, Equals, 1) 4294 case "storesvc-download": 4295 snapName := op.name 4296 c.Check(s.fakeStore.downloads[0], DeepEquals, fakeDownload{ 4297 macaroon: "macaroon", 4298 name: "some-snap", 4299 target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 4300 }, Commentf(snapName)) 4301 } 4302 } 4303 } 4304 4305 func (s *snapmgrTestSuite) TestUpdateToRevisionRememberedUserRunThrough(c *C) { 4306 s.state.Lock() 4307 defer s.state.Unlock() 4308 4309 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 4310 Active: true, 4311 Sequence: []*snap.SideInfo{ 4312 {RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"}, 4313 }, 4314 Current: snap.R(5), 4315 SnapType: "app", 4316 UserID: 1, 4317 }) 4318 4319 chg := s.state.NewChange("refresh", "refresh a snap") 4320 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(11)}, 0, snapstate.Flags{}) 4321 c.Assert(err, IsNil) 4322 chg.AddAll(ts) 4323 4324 s.state.Unlock() 4325 defer s.se.Stop() 4326 s.settle(c) 4327 s.state.Lock() 4328 4329 c.Assert(chg.Status(), Equals, state.DoneStatus) 4330 c.Assert(chg.Err(), IsNil) 4331 4332 for _, op := range s.fakeBackend.ops { 4333 switch op.op { 4334 case "storesvc-snap-action:action": 4335 c.Check(op.userID, Equals, 1) 4336 case "storesvc-download": 4337 snapName := op.name 4338 c.Check(s.fakeStore.downloads[0], DeepEquals, fakeDownload{ 4339 macaroon: "macaroon", 4340 name: "some-snap", 4341 target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 4342 }, Commentf(snapName)) 4343 } 4344 } 4345 } 4346 4347 func (s *snapmgrTestSuite) TestUpdateModelKernelSwitchTrackRunThrough(c *C) { 4348 // use services-snap here to make sure services would be stopped/started appropriately 4349 si := snap.SideInfo{ 4350 RealName: "kernel", 4351 Revision: snap.R(7), 4352 SnapID: "kernel-id", 4353 } 4354 snaptest.MockSnap(c, `name: kernel`, &si) 4355 fi, err := os.Stat(snap.MountFile("kernel", si.Revision)) 4356 c.Assert(err, IsNil) 4357 refreshedDate := fi.ModTime() 4358 // look at disk 4359 r := snapstate.MockRevisionDate(nil) 4360 defer r() 4361 4362 s.state.Lock() 4363 defer s.state.Unlock() 4364 4365 r1 := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 4366 defer r1() 4367 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 4368 Active: true, 4369 Sequence: []*snap.SideInfo{&si}, 4370 Current: si.Revision, 4371 Channel: "18/stable", 4372 }) 4373 4374 chg := s.state.NewChange("refresh", "refresh a snap") 4375 ts, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "edge"}, s.user.ID, snapstate.Flags{}) 4376 c.Assert(err, IsNil) 4377 chg.AddAll(ts) 4378 4379 s.state.Unlock() 4380 defer s.se.Stop() 4381 s.settle(c) 4382 s.state.Lock() 4383 4384 c.Check(chg.Status(), Equals, state.DoneStatus) 4385 4386 c.Assert(len(s.fakeBackend.ops) > 2, Equals, true) 4387 c.Assert(s.fakeBackend.ops[:2], DeepEquals, fakeOps{ 4388 { 4389 op: "storesvc-snap-action", 4390 curSnaps: []store.CurrentSnap{{ 4391 InstanceName: "kernel", 4392 SnapID: "kernel-id", 4393 Revision: snap.R(7), 4394 TrackingChannel: "18/stable", 4395 RefreshedDate: refreshedDate, 4396 Epoch: snap.E("1*"), 4397 }}, 4398 userID: 1, 4399 }, { 4400 op: "storesvc-snap-action:action", 4401 action: store.SnapAction{ 4402 Action: "refresh", 4403 InstanceName: "kernel", 4404 SnapID: "kernel-id", 4405 Channel: "18/edge", 4406 Flags: store.SnapActionEnforceValidation, 4407 }, 4408 revno: snap.R(11), 4409 userID: 1, 4410 }, 4411 }) 4412 4413 // check progress 4414 task := ts.Tasks()[1] 4415 _, cur, total := task.Progress() 4416 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 4417 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 4418 4419 // verify snapSetup info 4420 var snapsup snapstate.SnapSetup 4421 err = task.Get("snap-setup", &snapsup) 4422 c.Assert(err, IsNil) 4423 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 4424 Channel: "18/edge", 4425 UserID: s.user.ID, 4426 4427 SnapPath: filepath.Join(dirs.SnapBlobDir, "kernel_11.snap"), 4428 DownloadInfo: &snap.DownloadInfo{ 4429 DownloadURL: "https://some-server.com/some/path.snap", 4430 }, 4431 SideInfo: snapsup.SideInfo, 4432 Type: snap.TypeKernel, 4433 PlugsOnly: true, 4434 }) 4435 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 4436 RealName: "kernel", 4437 Revision: snap.R(11), 4438 Channel: "18/edge", 4439 SnapID: "kernel-id", 4440 }) 4441 4442 // verify snaps in the system state 4443 var snapst snapstate.SnapState 4444 err = snapstate.Get(s.state, "kernel", &snapst) 4445 c.Assert(err, IsNil) 4446 4447 c.Assert(snapst.Active, Equals, true) 4448 c.Assert(snapst.Channel, Equals, "18/edge") 4449 c.Assert(snapst.Sequence, HasLen, 2) 4450 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 4451 RealName: "kernel", 4452 SnapID: "kernel-id", 4453 Channel: "", 4454 Revision: snap.R(7), 4455 }) 4456 c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{ 4457 RealName: "kernel", 4458 Channel: "18/edge", 4459 SnapID: "kernel-id", 4460 Revision: snap.R(11), 4461 }) 4462 } 4463 4464 func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsNoUserRunThrough(c *C) { 4465 s.state.Lock() 4466 defer s.state.Unlock() 4467 4468 snapstate.Set(s.state, "core", &snapstate.SnapState{ 4469 Active: true, 4470 Sequence: []*snap.SideInfo{ 4471 {RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"}, 4472 }, 4473 Current: snap.R(1), 4474 SnapType: "os", 4475 }) 4476 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 4477 Active: true, 4478 Sequence: []*snap.SideInfo{ 4479 {RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"}, 4480 }, 4481 Current: snap.R(5), 4482 SnapType: "app", 4483 UserID: 1, 4484 }) 4485 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 4486 Active: true, 4487 Sequence: []*snap.SideInfo{ 4488 {RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"}, 4489 }, 4490 Current: snap.R(2), 4491 SnapType: "app", 4492 UserID: 2, 4493 }) 4494 4495 chg := s.state.NewChange("refresh", "refresh all snaps") 4496 // no user is passed to use for UpdateMany 4497 updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil) 4498 c.Assert(err, IsNil) 4499 for _, ts := range tts { 4500 chg.AddAll(ts) 4501 } 4502 c.Check(updated, HasLen, 3) 4503 4504 s.state.Unlock() 4505 defer s.se.Stop() 4506 s.settle(c) 4507 s.state.Lock() 4508 4509 c.Assert(chg.Status(), Equals, state.DoneStatus) 4510 c.Assert(chg.Err(), IsNil) 4511 4512 macaroonMap := map[string]string{ 4513 "core": "", 4514 "some-snap": "macaroon", 4515 "services-snap": "macaroon2", 4516 } 4517 4518 seen := make(map[string]int) 4519 ir := 0 4520 di := 0 4521 for _, op := range s.fakeBackend.ops { 4522 switch op.op { 4523 case "storesvc-snap-action": 4524 ir++ 4525 c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{ 4526 { 4527 InstanceName: "core", 4528 SnapID: "core-snap-id", 4529 Revision: snap.R(1), 4530 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1), 4531 Epoch: snap.E("1*"), 4532 }, 4533 { 4534 InstanceName: "services-snap", 4535 SnapID: "services-snap-id", 4536 Revision: snap.R(2), 4537 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2), 4538 Epoch: snap.E("0"), 4539 }, 4540 { 4541 InstanceName: "some-snap", 4542 SnapID: "some-snap-id", 4543 Revision: snap.R(5), 4544 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5), 4545 Epoch: snap.E("1*"), 4546 }, 4547 }) 4548 case "storesvc-snap-action:action": 4549 snapID := op.action.SnapID 4550 seen[snapID] = op.userID 4551 case "storesvc-download": 4552 snapName := op.name 4553 fakeDl := s.fakeStore.downloads[di] 4554 // check target path separately and clear it 4555 c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName))) 4556 fakeDl.target = "" 4557 c.Check(fakeDl, DeepEquals, fakeDownload{ 4558 macaroon: macaroonMap[snapName], 4559 name: snapName, 4560 }, Commentf(snapName)) 4561 di++ 4562 } 4563 } 4564 c.Check(ir, Equals, 2) 4565 // we check all snaps with each user 4566 c.Check(seen["some-snap-id"], Equals, 1) 4567 c.Check(seen["services-snap-id"], Equals, 2) 4568 // coalesced with one of the others 4569 c.Check(seen["core-snap-id"] > 0, Equals, true) 4570 } 4571 4572 func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserRunThrough(c *C) { 4573 s.state.Lock() 4574 defer s.state.Unlock() 4575 4576 snapstate.Set(s.state, "core", &snapstate.SnapState{ 4577 Active: true, 4578 Sequence: []*snap.SideInfo{ 4579 {RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"}, 4580 }, 4581 Current: snap.R(1), 4582 SnapType: "os", 4583 }) 4584 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 4585 Active: true, 4586 Sequence: []*snap.SideInfo{ 4587 {RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"}, 4588 }, 4589 Current: snap.R(5), 4590 SnapType: "app", 4591 UserID: 1, 4592 }) 4593 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 4594 Active: true, 4595 Sequence: []*snap.SideInfo{ 4596 {RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"}, 4597 }, 4598 Current: snap.R(2), 4599 SnapType: "app", 4600 UserID: 2, 4601 }) 4602 4603 chg := s.state.NewChange("refresh", "refresh all snaps") 4604 // do UpdateMany using user 2 as fallback 4605 updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 2, nil) 4606 c.Assert(err, IsNil) 4607 for _, ts := range tts { 4608 chg.AddAll(ts) 4609 } 4610 c.Check(updated, HasLen, 3) 4611 4612 s.state.Unlock() 4613 defer s.se.Stop() 4614 s.settle(c) 4615 s.state.Lock() 4616 4617 c.Assert(chg.Status(), Equals, state.DoneStatus) 4618 c.Assert(chg.Err(), IsNil) 4619 4620 macaroonMap := map[string]string{ 4621 "core": "macaroon2", 4622 "some-snap": "macaroon", 4623 "services-snap": "macaroon2", 4624 } 4625 4626 type snapIDuserID struct { 4627 snapID string 4628 userID int 4629 } 4630 seen := make(map[snapIDuserID]bool) 4631 ir := 0 4632 di := 0 4633 for _, op := range s.fakeBackend.ops { 4634 switch op.op { 4635 case "storesvc-snap-action": 4636 ir++ 4637 c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{ 4638 { 4639 InstanceName: "core", 4640 SnapID: "core-snap-id", 4641 Revision: snap.R(1), 4642 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1), 4643 Epoch: snap.E("1*"), 4644 }, 4645 { 4646 InstanceName: "services-snap", 4647 SnapID: "services-snap-id", 4648 Revision: snap.R(2), 4649 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2), 4650 Epoch: snap.E("0"), 4651 }, 4652 { 4653 InstanceName: "some-snap", 4654 SnapID: "some-snap-id", 4655 Revision: snap.R(5), 4656 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5), 4657 Epoch: snap.E("1*"), 4658 }, 4659 }) 4660 case "storesvc-snap-action:action": 4661 snapID := op.action.SnapID 4662 seen[snapIDuserID{snapID: snapID, userID: op.userID}] = true 4663 case "storesvc-download": 4664 snapName := op.name 4665 fakeDl := s.fakeStore.downloads[di] 4666 // check target path separately and clear it 4667 c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName))) 4668 fakeDl.target = "" 4669 c.Check(fakeDl, DeepEquals, fakeDownload{ 4670 macaroon: macaroonMap[snapName], 4671 name: snapName, 4672 }, Commentf(snapName)) 4673 di++ 4674 } 4675 } 4676 c.Check(ir, Equals, 2) 4677 // we check all snaps with each user 4678 c.Check(seen, DeepEquals, map[snapIDuserID]bool{ 4679 {snapID: "core-snap-id", userID: 2}: true, 4680 {snapID: "some-snap-id", userID: 1}: true, 4681 {snapID: "services-snap-id", userID: 2}: true, 4682 }) 4683 4684 var coreState, snapState snapstate.SnapState 4685 // user in SnapState was preserved 4686 err = snapstate.Get(s.state, "some-snap", &snapState) 4687 c.Assert(err, IsNil) 4688 c.Check(snapState.UserID, Equals, 1) 4689 c.Check(snapState.Current, DeepEquals, snap.R(11)) 4690 4691 // user in SnapState was set 4692 err = snapstate.Get(s.state, "core", &coreState) 4693 c.Assert(err, IsNil) 4694 c.Check(coreState.UserID, Equals, 2) 4695 c.Check(coreState.Current, DeepEquals, snap.R(11)) 4696 4697 } 4698 4699 func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserWithNoStoreAuthRunThrough(c *C) { 4700 s.state.Lock() 4701 defer s.state.Unlock() 4702 4703 snapstate.Set(s.state, "core", &snapstate.SnapState{ 4704 Active: true, 4705 Sequence: []*snap.SideInfo{ 4706 {RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"}, 4707 }, 4708 Current: snap.R(1), 4709 SnapType: "os", 4710 }) 4711 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 4712 Active: true, 4713 Sequence: []*snap.SideInfo{ 4714 {RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"}, 4715 }, 4716 Current: snap.R(5), 4717 SnapType: "app", 4718 UserID: 1, 4719 }) 4720 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 4721 Active: true, 4722 Sequence: []*snap.SideInfo{ 4723 {RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"}, 4724 }, 4725 Current: snap.R(2), 4726 SnapType: "app", 4727 UserID: 3, 4728 }) 4729 4730 chg := s.state.NewChange("refresh", "refresh all snaps") 4731 // no user is passed to use for UpdateMany 4732 updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil) 4733 c.Assert(err, IsNil) 4734 for _, ts := range tts { 4735 chg.AddAll(ts) 4736 } 4737 c.Check(updated, HasLen, 3) 4738 4739 s.state.Unlock() 4740 defer s.se.Stop() 4741 s.settle(c) 4742 s.state.Lock() 4743 4744 c.Assert(chg.Status(), Equals, state.DoneStatus) 4745 c.Assert(chg.Err(), IsNil) 4746 4747 macaroonMap := map[string]string{ 4748 "core": "", 4749 "some-snap": "macaroon", 4750 "services-snap": "", 4751 } 4752 4753 seen := make(map[string]int) 4754 ir := 0 4755 di := 0 4756 for _, op := range s.fakeBackend.ops { 4757 switch op.op { 4758 case "storesvc-snap-action": 4759 ir++ 4760 c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{ 4761 { 4762 InstanceName: "core", 4763 SnapID: "core-snap-id", 4764 Revision: snap.R(1), 4765 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1), 4766 Epoch: snap.E("1*"), 4767 }, 4768 { 4769 InstanceName: "services-snap", 4770 SnapID: "services-snap-id", 4771 Revision: snap.R(2), 4772 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2), 4773 Epoch: snap.E("0"), 4774 }, 4775 { 4776 InstanceName: "some-snap", 4777 SnapID: "some-snap-id", 4778 Revision: snap.R(5), 4779 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5), 4780 Epoch: snap.E("1*"), 4781 }, 4782 }) 4783 case "storesvc-snap-action:action": 4784 snapID := op.action.SnapID 4785 if _, ok := seen[snapID]; !ok { 4786 seen[snapID] = op.userID 4787 } 4788 case "storesvc-download": 4789 snapName := op.name 4790 fakeDl := s.fakeStore.downloads[di] 4791 // check target path separately and clear it 4792 c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName))) 4793 fakeDl.target = "" 4794 c.Check(fakeDl, DeepEquals, fakeDownload{ 4795 macaroon: macaroonMap[snapName], 4796 name: snapName, 4797 }, Commentf(snapName)) 4798 di++ 4799 } 4800 } 4801 c.Check(ir, Equals, 1) 4802 // we check all snaps with each user 4803 c.Check(seen["some-snap-id"], Equals, 1) 4804 // coalesced with request for 1 4805 c.Check(seen["services-snap-id"], Equals, 1) 4806 c.Check(seen["core-snap-id"], Equals, 1) 4807 } 4808 4809 func (s *snapmgrTestSuite) TestUpdateUndoRunThrough(c *C) { 4810 si := snap.SideInfo{ 4811 RealName: "some-snap", 4812 SnapID: "some-snap-id", 4813 Revision: snap.R(7), 4814 } 4815 4816 s.state.Lock() 4817 defer s.state.Unlock() 4818 4819 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 4820 Active: true, 4821 Sequence: []*snap.SideInfo{&si}, 4822 Current: si.Revision, 4823 SnapType: "app", 4824 }) 4825 4826 chg := s.state.NewChange("install", "install a snap") 4827 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 4828 c.Assert(err, IsNil) 4829 chg.AddAll(ts) 4830 4831 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "/some-snap/11") 4832 4833 s.state.Unlock() 4834 defer s.se.Stop() 4835 s.settle(c) 4836 s.state.Lock() 4837 4838 expected := fakeOps{ 4839 { 4840 op: "storesvc-snap-action", 4841 curSnaps: []store.CurrentSnap{{ 4842 InstanceName: "some-snap", 4843 SnapID: "some-snap-id", 4844 Revision: snap.R(7), 4845 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 4846 Epoch: snap.E("1*"), 4847 }}, 4848 userID: 1, 4849 }, 4850 { 4851 op: "storesvc-snap-action:action", 4852 action: store.SnapAction{ 4853 Action: "refresh", 4854 InstanceName: "some-snap", 4855 SnapID: "some-snap-id", 4856 Channel: "some-channel", 4857 Flags: store.SnapActionEnforceValidation, 4858 }, 4859 revno: snap.R(11), 4860 userID: 1, 4861 }, 4862 { 4863 op: "storesvc-download", 4864 name: "some-snap", 4865 }, 4866 { 4867 op: "validate-snap:Doing", 4868 name: "some-snap", 4869 revno: snap.R(11), 4870 }, 4871 { 4872 op: "current", 4873 old: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 4874 }, 4875 { 4876 op: "open-snap-file", 4877 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 4878 sinfo: snap.SideInfo{ 4879 RealName: "some-snap", 4880 SnapID: "some-snap-id", 4881 Channel: "some-channel", 4882 Revision: snap.R(11), 4883 }, 4884 }, 4885 { 4886 op: "setup-snap", 4887 name: "some-snap", 4888 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 4889 revno: snap.R(11), 4890 }, 4891 { 4892 op: "remove-snap-aliases", 4893 name: "some-snap", 4894 }, 4895 { 4896 op: "unlink-snap", 4897 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 4898 }, 4899 { 4900 op: "copy-data", 4901 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 4902 old: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 4903 }, 4904 { 4905 op: "setup-profiles:Doing", 4906 name: "some-snap", 4907 revno: snap.R(11), 4908 }, 4909 { 4910 op: "candidate", 4911 sinfo: snap.SideInfo{ 4912 RealName: "some-snap", 4913 SnapID: "some-snap-id", 4914 Channel: "some-channel", 4915 Revision: snap.R(11), 4916 }, 4917 }, 4918 { 4919 op: "link-snap.failed", 4920 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 4921 }, 4922 { 4923 op: "unlink-snap", 4924 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 4925 }, 4926 { 4927 op: "setup-profiles:Undoing", 4928 name: "some-snap", 4929 revno: snap.R(11), 4930 }, 4931 { 4932 op: "undo-copy-snap-data", 4933 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 4934 old: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 4935 }, 4936 { 4937 op: "link-snap", 4938 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 4939 }, 4940 { 4941 op: "update-aliases", 4942 }, 4943 { 4944 op: "undo-setup-snap", 4945 name: "some-snap", 4946 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 4947 stype: "app", 4948 }, 4949 { 4950 op: "remove-snap-dir", 4951 name: "some-snap", 4952 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 4953 }, 4954 } 4955 4956 // ensure all our tasks ran 4957 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 4958 macaroon: s.user.StoreMacaroon, 4959 name: "some-snap", 4960 target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 4961 }}) 4962 // start with an easier-to-read error if this fails: 4963 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 4964 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 4965 4966 // verify snaps in the system state 4967 var snapst snapstate.SnapState 4968 err = snapstate.Get(s.state, "some-snap", &snapst) 4969 c.Assert(err, IsNil) 4970 4971 c.Assert(snapst.Active, Equals, true) 4972 c.Assert(snapst.Sequence, HasLen, 1) 4973 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 4974 RealName: "some-snap", 4975 SnapID: "some-snap-id", 4976 Channel: "", 4977 Revision: snap.R(7), 4978 }) 4979 } 4980 4981 func lastWithLane(tasks []*state.Task) *state.Task { 4982 for i := len(tasks) - 1; i >= 0; i-- { 4983 if lanes := tasks[i].Lanes(); len(lanes) == 1 && lanes[0] != 0 { 4984 return tasks[i] 4985 } 4986 } 4987 return nil 4988 } 4989 4990 func (s *snapmgrTestSuite) TestUpdateUndoRestoresRevisionConfig(c *C) { 4991 var errorTaskExecuted bool 4992 4993 // overwrite error-trigger task handler with custom one for this test 4994 erroringHandler := func(task *state.Task, _ *tomb.Tomb) error { 4995 st := task.State() 4996 st.Lock() 4997 defer st.Unlock() 4998 4999 // modify current config of some-snap 5000 tr := config.NewTransaction(st) 5001 tr.Set("some-snap", "foo", "canary") 5002 tr.Commit() 5003 5004 errorTaskExecuted = true 5005 return errors.New("error out") 5006 } 5007 s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil) 5008 5009 si := snap.SideInfo{ 5010 RealName: "some-snap", 5011 SnapID: "some-snap-id", 5012 Revision: snap.R(7), 5013 } 5014 si2 := snap.SideInfo{ 5015 RealName: "some-snap", 5016 SnapID: "some-snap-id", 5017 Revision: snap.R(6), 5018 } 5019 5020 s.state.Lock() 5021 defer s.state.Unlock() 5022 5023 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5024 Active: true, 5025 Sequence: []*snap.SideInfo{&si2, &si}, 5026 Channel: "stable", 5027 Current: si.Revision, 5028 SnapType: "app", 5029 }) 5030 5031 // set some configuration 5032 tr := config.NewTransaction(s.state) 5033 tr.Set("some-snap", "foo", "revision 7 value") 5034 tr.Commit() 5035 config.SaveRevisionConfig(s.state, "some-snap", snap.R(7)) 5036 5037 chg := s.state.NewChange("install", "install a snap") 5038 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 5039 c.Assert(err, IsNil) 5040 chg.AddAll(ts) 5041 5042 last := lastWithLane(ts.Tasks()) 5043 c.Assert(last, NotNil) 5044 5045 terr := s.state.NewTask("error-trigger", "provoking total undo") 5046 terr.WaitFor(last) 5047 terr.JoinLane(last.Lanes()[0]) 5048 chg.AddTask(terr) 5049 5050 s.state.Unlock() 5051 defer s.se.Stop() 5052 s.settle(c) 5053 s.state.Lock() 5054 5055 c.Check(chg.Status(), Equals, state.ErrorStatus) 5056 c.Check(errorTaskExecuted, Equals, true) 5057 5058 // after undoing the update some-snap config should be restored to that of rev.7 5059 var val string 5060 tr = config.NewTransaction(s.state) 5061 c.Assert(tr.Get("some-snap", "foo", &val), IsNil) 5062 c.Check(val, Equals, "revision 7 value") 5063 } 5064 5065 func (s *snapmgrTestSuite) TestUpdateTotalUndoRunThrough(c *C) { 5066 si := snap.SideInfo{ 5067 RealName: "some-snap", 5068 SnapID: "some-snap-id", 5069 Revision: snap.R(7), 5070 } 5071 5072 s.state.Lock() 5073 defer s.state.Unlock() 5074 5075 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5076 Active: true, 5077 Sequence: []*snap.SideInfo{&si}, 5078 Channel: "stable", 5079 Current: si.Revision, 5080 SnapType: "app", 5081 }) 5082 5083 chg := s.state.NewChange("install", "install a snap") 5084 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 5085 c.Assert(err, IsNil) 5086 chg.AddAll(ts) 5087 5088 // We need to make it not be rerefresh, and we could do just 5089 // that but instead we do the 'right' thing and attach it to 5090 // the last task that's on a lane. 5091 last := lastWithLane(ts.Tasks()) 5092 c.Assert(last, NotNil) 5093 5094 terr := s.state.NewTask("error-trigger", "provoking total undo") 5095 terr.WaitFor(last) 5096 terr.JoinLane(last.Lanes()[0]) 5097 chg.AddTask(terr) 5098 5099 s.state.Unlock() 5100 defer s.se.Stop() 5101 s.settle(c) 5102 s.state.Lock() 5103 5104 expected := fakeOps{ 5105 { 5106 op: "storesvc-snap-action", 5107 curSnaps: []store.CurrentSnap{{ 5108 InstanceName: "some-snap", 5109 SnapID: "some-snap-id", 5110 Revision: snap.R(7), 5111 TrackingChannel: "stable", 5112 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 5113 Epoch: snap.E("1*"), 5114 }}, 5115 userID: 1, 5116 }, 5117 { 5118 op: "storesvc-snap-action:action", 5119 action: store.SnapAction{ 5120 Action: "refresh", 5121 InstanceName: "some-snap", 5122 SnapID: "some-snap-id", 5123 Channel: "some-channel", 5124 Flags: store.SnapActionEnforceValidation, 5125 }, 5126 revno: snap.R(11), 5127 userID: 1, 5128 }, 5129 { 5130 op: "storesvc-download", 5131 name: "some-snap", 5132 }, 5133 { 5134 op: "validate-snap:Doing", 5135 name: "some-snap", 5136 revno: snap.R(11), 5137 }, 5138 { 5139 op: "current", 5140 old: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 5141 }, 5142 { 5143 op: "open-snap-file", 5144 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 5145 sinfo: snap.SideInfo{ 5146 RealName: "some-snap", 5147 SnapID: "some-snap-id", 5148 Channel: "some-channel", 5149 Revision: snap.R(11), 5150 }, 5151 }, 5152 { 5153 op: "setup-snap", 5154 name: "some-snap", 5155 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 5156 revno: snap.R(11), 5157 }, 5158 { 5159 op: "remove-snap-aliases", 5160 name: "some-snap", 5161 }, 5162 { 5163 op: "unlink-snap", 5164 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 5165 }, 5166 { 5167 op: "copy-data", 5168 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 5169 old: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 5170 }, 5171 { 5172 op: "setup-profiles:Doing", 5173 name: "some-snap", 5174 revno: snap.R(11), 5175 }, 5176 { 5177 op: "candidate", 5178 sinfo: snap.SideInfo{ 5179 RealName: "some-snap", 5180 SnapID: "some-snap-id", 5181 Channel: "some-channel", 5182 Revision: snap.R(11), 5183 }, 5184 }, 5185 { 5186 op: "link-snap", 5187 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 5188 }, 5189 { 5190 op: "auto-connect:Doing", 5191 name: "some-snap", 5192 revno: snap.R(11), 5193 }, 5194 { 5195 op: "update-aliases", 5196 }, 5197 // undoing everything from here down... 5198 { 5199 op: "remove-snap-aliases", 5200 name: "some-snap", 5201 }, 5202 { 5203 op: "unlink-snap", 5204 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 5205 }, 5206 { 5207 op: "setup-profiles:Undoing", 5208 name: "some-snap", 5209 revno: snap.R(11), 5210 }, 5211 { 5212 op: "undo-copy-snap-data", 5213 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 5214 old: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 5215 }, 5216 { 5217 op: "link-snap", 5218 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 5219 }, 5220 { 5221 op: "update-aliases", 5222 }, 5223 { 5224 op: "undo-setup-snap", 5225 name: "some-snap", 5226 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 5227 stype: "app", 5228 }, 5229 { 5230 op: "remove-snap-dir", 5231 name: "some-snap", 5232 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 5233 }, 5234 } 5235 5236 // ensure all our tasks ran 5237 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 5238 macaroon: s.user.StoreMacaroon, 5239 name: "some-snap", 5240 target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 5241 }}) 5242 // friendlier failure first 5243 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 5244 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 5245 5246 // verify snaps in the system state 5247 var snapst snapstate.SnapState 5248 err = snapstate.Get(s.state, "some-snap", &snapst) 5249 c.Assert(err, IsNil) 5250 5251 c.Assert(snapst.Active, Equals, true) 5252 c.Assert(snapst.Channel, Equals, "stable") 5253 c.Assert(snapst.Sequence, HasLen, 1) 5254 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 5255 RealName: "some-snap", 5256 SnapID: "some-snap-id", 5257 Channel: "", 5258 Revision: snap.R(7), 5259 }) 5260 } 5261 5262 func (s *snapmgrTestSuite) TestUpdateSameRevision(c *C) { 5263 si := snap.SideInfo{ 5264 RealName: "some-snap", 5265 SnapID: "some-snap-id", 5266 Revision: snap.R(7), 5267 } 5268 5269 s.state.Lock() 5270 defer s.state.Unlock() 5271 5272 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5273 Active: true, 5274 Sequence: []*snap.SideInfo{&si}, 5275 Channel: "channel-for-7", 5276 Current: si.Revision, 5277 }) 5278 5279 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{}) 5280 c.Assert(err, Equals, store.ErrNoUpdateAvailable) 5281 } 5282 5283 // A noResultsStore returns no results for install/refresh requests 5284 type noResultsStore struct { 5285 *fakeStore 5286 } 5287 5288 func (n noResultsStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) { 5289 return nil, &store.SnapActionError{NoResults: true} 5290 } 5291 5292 func (s *snapmgrTestSuite) TestUpdateNoStoreResults(c *C) { 5293 s.state.Lock() 5294 defer s.state.Unlock() 5295 5296 snapstate.ReplaceStore(s.state, noResultsStore{fakeStore: s.fakeStore}) 5297 5298 // this is an atypical case in which the store didn't return 5299 // an error nor a result, we are defensive and return 5300 // a reasonable error 5301 si := snap.SideInfo{ 5302 RealName: "some-snap", 5303 SnapID: "some-snap-id", 5304 Revision: snap.R(7), 5305 } 5306 5307 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5308 Active: true, 5309 Sequence: []*snap.SideInfo{&si}, 5310 Channel: "channel-for-7", 5311 Current: si.Revision, 5312 }) 5313 5314 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{}) 5315 c.Assert(err, Equals, snapstate.ErrMissingExpectedResult) 5316 } 5317 5318 func (s *snapmgrTestSuite) TestUpdateNoStoreResultsWithChannelChange(c *C) { 5319 s.state.Lock() 5320 defer s.state.Unlock() 5321 5322 snapstate.ReplaceStore(s.state, noResultsStore{fakeStore: s.fakeStore}) 5323 5324 // this is an atypical case in which the store didn't return 5325 // an error nor a result, we are defensive and return 5326 // a reasonable error 5327 si := snap.SideInfo{ 5328 RealName: "some-snap", 5329 SnapID: "some-snap-id", 5330 Revision: snap.R(7), 5331 } 5332 5333 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5334 Active: true, 5335 Sequence: []*snap.SideInfo{&si}, 5336 Channel: "channel-for-9", 5337 Current: si.Revision, 5338 }) 5339 5340 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{}) 5341 c.Assert(err, Equals, snapstate.ErrMissingExpectedResult) 5342 } 5343 5344 func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchesChannel(c *C) { 5345 si := snap.SideInfo{ 5346 RealName: "some-snap", 5347 SnapID: "some-snap-id", 5348 Revision: snap.R(7), 5349 } 5350 5351 s.state.Lock() 5352 defer s.state.Unlock() 5353 5354 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5355 Active: true, 5356 Sequence: []*snap.SideInfo{&si}, 5357 Channel: "other-chanenl", 5358 Current: si.Revision, 5359 }) 5360 5361 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{}) 5362 c.Assert(err, IsNil) 5363 c.Check(ts.Tasks(), HasLen, 1) 5364 c.Check(ts.Tasks()[0].Kind(), Equals, "switch-snap-channel") 5365 } 5366 5367 func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchesChannelConflict(c *C) { 5368 si := snap.SideInfo{ 5369 RealName: "some-snap", 5370 SnapID: "some-snap-id", 5371 Revision: snap.R(7), 5372 } 5373 5374 s.state.Lock() 5375 defer s.state.Unlock() 5376 5377 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5378 Active: true, 5379 Sequence: []*snap.SideInfo{&si}, 5380 Channel: "other-channel", 5381 Current: si.Revision, 5382 }) 5383 5384 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{}) 5385 c.Assert(err, IsNil) 5386 // make it visible 5387 s.state.NewChange("refresh", "refresh a snap").AddAll(ts) 5388 5389 _, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{}) 5390 c.Check(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`) 5391 } 5392 5393 func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchChannelRunThrough(c *C) { 5394 si := snap.SideInfo{ 5395 RealName: "some-snap", 5396 SnapID: "some-snap-id", 5397 Channel: "other-channel", 5398 Revision: snap.R(7), 5399 } 5400 5401 s.state.Lock() 5402 defer s.state.Unlock() 5403 5404 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5405 Active: true, 5406 Sequence: []*snap.SideInfo{&si}, 5407 Channel: "other-channel", 5408 Current: si.Revision, 5409 }) 5410 5411 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{}) 5412 c.Assert(err, IsNil) 5413 chg := s.state.NewChange("refresh", "refresh a snap") 5414 chg.AddAll(ts) 5415 5416 s.state.Unlock() 5417 defer s.se.Stop() 5418 s.settle(c) 5419 s.state.Lock() 5420 5421 expected := fakeOps{ 5422 // we just expect the "storesvc-snap-action" ops, we 5423 // don't have a fakeOp for switchChannel because it has 5424 // not a backend method, it just manipulates the state 5425 { 5426 op: "storesvc-snap-action", 5427 curSnaps: []store.CurrentSnap{{ 5428 InstanceName: "some-snap", 5429 SnapID: "some-snap-id", 5430 Revision: snap.R(7), 5431 TrackingChannel: "other-channel", 5432 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 5433 Epoch: snap.E("1*"), 5434 }}, 5435 userID: 1, 5436 }, 5437 5438 { 5439 op: "storesvc-snap-action:action", 5440 action: store.SnapAction{ 5441 Action: "refresh", 5442 InstanceName: "some-snap", 5443 SnapID: "some-snap-id", 5444 Channel: "channel-for-7", 5445 Flags: store.SnapActionEnforceValidation, 5446 }, 5447 userID: 1, 5448 }, 5449 } 5450 5451 // start with an easier-to-read error if this fails: 5452 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 5453 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 5454 5455 // verify snapSetup info 5456 var snapsup snapstate.SnapSetup 5457 task := ts.Tasks()[0] 5458 err = task.Get("snap-setup", &snapsup) 5459 c.Assert(err, IsNil) 5460 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 5461 Channel: "channel-for-7", 5462 SideInfo: snapsup.SideInfo, 5463 }) 5464 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 5465 RealName: "some-snap", 5466 SnapID: "some-snap-id", 5467 Revision: snap.R(7), 5468 Channel: "channel-for-7", 5469 }) 5470 5471 // verify snaps in the system state 5472 var snapst snapstate.SnapState 5473 err = snapstate.Get(s.state, "some-snap", &snapst) 5474 c.Assert(err, IsNil) 5475 5476 c.Assert(snapst.Active, Equals, true) 5477 c.Assert(snapst.Sequence, HasLen, 1) 5478 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 5479 RealName: "some-snap", 5480 SnapID: "some-snap-id", 5481 Channel: "channel-for-7", 5482 Revision: snap.R(7), 5483 }) 5484 } 5485 5486 func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidation(c *C) { 5487 si := snap.SideInfo{ 5488 RealName: "some-snap", 5489 SnapID: "some-snap-id", 5490 Revision: snap.R(7), 5491 } 5492 5493 s.state.Lock() 5494 defer s.state.Unlock() 5495 5496 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5497 Active: true, 5498 Sequence: []*snap.SideInfo{&si}, 5499 Channel: "channel-for-7", 5500 Current: si.Revision, 5501 }) 5502 5503 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true}) 5504 c.Assert(err, IsNil) 5505 c.Check(ts.Tasks(), HasLen, 1) 5506 c.Check(ts.Tasks()[0].Kind(), Equals, "toggle-snap-flags") 5507 } 5508 5509 func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidationConflict(c *C) { 5510 si := snap.SideInfo{ 5511 RealName: "some-snap", 5512 SnapID: "some-snap-id", 5513 Revision: snap.R(7), 5514 } 5515 5516 s.state.Lock() 5517 defer s.state.Unlock() 5518 5519 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5520 Active: true, 5521 Sequence: []*snap.SideInfo{&si}, 5522 Channel: "channel-for-7", 5523 Current: si.Revision, 5524 }) 5525 5526 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true}) 5527 c.Assert(err, IsNil) 5528 // make it visible 5529 s.state.NewChange("refresh", "refresh a snap").AddAll(ts) 5530 5531 _, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true}) 5532 c.Check(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`) 5533 5534 } 5535 5536 func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidationRunThrough(c *C) { 5537 si := snap.SideInfo{ 5538 RealName: "some-snap", 5539 SnapID: "some-snap-id", 5540 Revision: snap.R(7), 5541 Channel: "channel-for-7", 5542 } 5543 5544 s.state.Lock() 5545 defer s.state.Unlock() 5546 5547 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5548 Active: true, 5549 Sequence: []*snap.SideInfo{&si}, 5550 Channel: "channel-for-7", 5551 Current: si.Revision, 5552 }) 5553 5554 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true}) 5555 c.Assert(err, IsNil) 5556 5557 chg := s.state.NewChange("refresh", "refresh a snap") 5558 chg.AddAll(ts) 5559 5560 s.state.Unlock() 5561 defer s.se.Stop() 5562 s.settle(c) 5563 s.state.Lock() 5564 5565 // verify snapSetup info 5566 var snapsup snapstate.SnapSetup 5567 task := ts.Tasks()[0] 5568 err = task.Get("snap-setup", &snapsup) 5569 c.Assert(err, IsNil) 5570 c.Check(snapsup, DeepEquals, snapstate.SnapSetup{ 5571 SideInfo: snapsup.SideInfo, 5572 Flags: snapstate.Flags{ 5573 IgnoreValidation: true, 5574 }, 5575 }) 5576 c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 5577 RealName: "some-snap", 5578 SnapID: "some-snap-id", 5579 Revision: snap.R(7), 5580 Channel: "channel-for-7", 5581 }) 5582 5583 // verify snaps in the system state 5584 var snapst snapstate.SnapState 5585 err = snapstate.Get(s.state, "some-snap", &snapst) 5586 c.Assert(err, IsNil) 5587 5588 c.Check(snapst.Active, Equals, true) 5589 c.Check(snapst.Sequence, HasLen, 1) 5590 c.Check(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 5591 RealName: "some-snap", 5592 SnapID: "some-snap-id", 5593 Channel: "channel-for-7", 5594 Revision: snap.R(7), 5595 }) 5596 c.Check(snapst.IgnoreValidation, Equals, true) 5597 } 5598 5599 func (s *snapmgrTestSuite) TestUpdateValidateRefreshesSaysNo(c *C) { 5600 si := snap.SideInfo{ 5601 RealName: "some-snap", 5602 SnapID: "some-snap-id", 5603 Revision: snap.R(7), 5604 } 5605 5606 s.state.Lock() 5607 defer s.state.Unlock() 5608 5609 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5610 Active: true, 5611 Sequence: []*snap.SideInfo{&si}, 5612 Current: si.Revision, 5613 }) 5614 5615 validateErr := errors.New("refresh control error") 5616 validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) { 5617 c.Check(refreshes, HasLen, 1) 5618 c.Check(refreshes[0].SnapID, Equals, "some-snap-id") 5619 c.Check(refreshes[0].Revision, Equals, snap.R(11)) 5620 c.Check(ignoreValidation, HasLen, 0) 5621 return nil, validateErr 5622 } 5623 // hook it up 5624 snapstate.ValidateRefreshes = validateRefreshes 5625 5626 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{}) 5627 c.Assert(err, Equals, validateErr) 5628 } 5629 5630 func (s *snapmgrTestSuite) TestUpdateValidateRefreshesSaysNoButIgnoreValidationIsSet(c *C) { 5631 si := snap.SideInfo{ 5632 RealName: "some-snap", 5633 SnapID: "some-snap-id", 5634 Revision: snap.R(7), 5635 } 5636 5637 s.state.Lock() 5638 defer s.state.Unlock() 5639 5640 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5641 Active: true, 5642 Sequence: []*snap.SideInfo{&si}, 5643 Current: si.Revision, 5644 SnapType: "app", 5645 }) 5646 5647 validateErr := errors.New("refresh control error") 5648 validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) { 5649 return nil, validateErr 5650 } 5651 // hook it up 5652 snapstate.ValidateRefreshes = validateRefreshes 5653 5654 flags := snapstate.Flags{JailMode: true, IgnoreValidation: true} 5655 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags) 5656 c.Assert(err, IsNil) 5657 5658 var snapsup snapstate.SnapSetup 5659 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 5660 c.Assert(err, IsNil) 5661 c.Check(snapsup.Flags, DeepEquals, flags.ForSnapSetup()) 5662 } 5663 5664 func (s *snapmgrTestSuite) TestUpdateIgnoreValidationSticky(c *C) { 5665 si := snap.SideInfo{ 5666 RealName: "some-snap", 5667 SnapID: "some-snap-id", 5668 Revision: snap.R(7), 5669 } 5670 5671 s.state.Lock() 5672 defer s.state.Unlock() 5673 5674 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5675 Active: true, 5676 Sequence: []*snap.SideInfo{&si}, 5677 Current: si.Revision, 5678 SnapType: "app", 5679 }) 5680 5681 validateErr := errors.New("refresh control error") 5682 validateRefreshesFail := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) { 5683 c.Check(refreshes, HasLen, 1) 5684 if len(ignoreValidation) == 0 { 5685 return nil, validateErr 5686 } 5687 c.Check(ignoreValidation, DeepEquals, map[string]bool{ 5688 "some-snap": true, 5689 }) 5690 return refreshes, nil 5691 } 5692 // hook it up 5693 snapstate.ValidateRefreshes = validateRefreshesFail 5694 5695 flags := snapstate.Flags{IgnoreValidation: true} 5696 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags) 5697 c.Assert(err, IsNil) 5698 5699 c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ 5700 op: "storesvc-snap-action", 5701 curSnaps: []store.CurrentSnap{{ 5702 InstanceName: "some-snap", 5703 SnapID: "some-snap-id", 5704 Revision: snap.R(7), 5705 IgnoreValidation: false, 5706 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 5707 Epoch: snap.E("1*"), 5708 }}, 5709 userID: 1, 5710 }) 5711 c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{ 5712 op: "storesvc-snap-action:action", 5713 revno: snap.R(11), 5714 action: store.SnapAction{ 5715 Action: "refresh", 5716 InstanceName: "some-snap", 5717 SnapID: "some-snap-id", 5718 Channel: "stable", 5719 Flags: store.SnapActionIgnoreValidation, 5720 }, 5721 userID: 1, 5722 }) 5723 5724 chg := s.state.NewChange("refresh", "refresh snap") 5725 chg.AddAll(ts) 5726 5727 s.state.Unlock() 5728 defer s.se.Stop() 5729 s.settle(c) 5730 s.state.Lock() 5731 5732 // verify snap has IgnoreValidation set 5733 var snapst snapstate.SnapState 5734 err = snapstate.Get(s.state, "some-snap", &snapst) 5735 c.Assert(err, IsNil) 5736 c.Check(snapst.IgnoreValidation, Equals, true) 5737 c.Check(snapst.Current, Equals, snap.R(11)) 5738 5739 s.fakeBackend.ops = nil 5740 s.fakeStore.refreshRevnos = map[string]snap.Revision{ 5741 "some-snap-id": snap.R(12), 5742 } 5743 _, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil) 5744 c.Assert(err, IsNil) 5745 c.Check(tts, HasLen, 2) 5746 verifyLastTasksetIsReRefresh(c, tts) 5747 5748 c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ 5749 op: "storesvc-snap-action", 5750 curSnaps: []store.CurrentSnap{{ 5751 InstanceName: "some-snap", 5752 SnapID: "some-snap-id", 5753 Revision: snap.R(11), 5754 TrackingChannel: "stable", 5755 IgnoreValidation: true, 5756 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 11), 5757 Epoch: snap.E("1*"), 5758 }}, 5759 userID: 1, 5760 }) 5761 c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{ 5762 op: "storesvc-snap-action:action", 5763 revno: snap.R(12), 5764 action: store.SnapAction{ 5765 Action: "refresh", 5766 InstanceName: "some-snap", 5767 SnapID: "some-snap-id", 5768 Flags: 0, 5769 }, 5770 userID: 1, 5771 }) 5772 5773 chg = s.state.NewChange("refresh", "refresh snaps") 5774 chg.AddAll(tts[0]) 5775 5776 s.state.Unlock() 5777 defer s.se.Stop() 5778 s.settle(c) 5779 s.state.Lock() 5780 5781 snapst = snapstate.SnapState{} 5782 err = snapstate.Get(s.state, "some-snap", &snapst) 5783 c.Assert(err, IsNil) 5784 c.Check(snapst.IgnoreValidation, Equals, true) 5785 c.Check(snapst.Current, Equals, snap.R(12)) 5786 5787 // reset ignore validation 5788 s.fakeBackend.ops = nil 5789 s.fakeStore.refreshRevnos = map[string]snap.Revision{ 5790 "some-snap-id": snap.R(11), 5791 } 5792 validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) { 5793 return refreshes, nil 5794 } 5795 // hook it up 5796 snapstate.ValidateRefreshes = validateRefreshes 5797 flags = snapstate.Flags{} 5798 ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags) 5799 c.Assert(err, IsNil) 5800 5801 c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ 5802 op: "storesvc-snap-action", 5803 curSnaps: []store.CurrentSnap{{ 5804 InstanceName: "some-snap", 5805 SnapID: "some-snap-id", 5806 Revision: snap.R(12), 5807 TrackingChannel: "stable", 5808 IgnoreValidation: true, 5809 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 12), 5810 Epoch: snap.E("1*"), 5811 }}, 5812 userID: 1, 5813 }) 5814 c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{ 5815 op: "storesvc-snap-action:action", 5816 revno: snap.R(11), 5817 action: store.SnapAction{ 5818 Action: "refresh", 5819 InstanceName: "some-snap", 5820 SnapID: "some-snap-id", 5821 Channel: "stable", 5822 Flags: store.SnapActionEnforceValidation, 5823 }, 5824 userID: 1, 5825 }) 5826 5827 chg = s.state.NewChange("refresh", "refresh snap") 5828 chg.AddAll(ts) 5829 5830 s.state.Unlock() 5831 defer s.se.Stop() 5832 s.settle(c) 5833 s.state.Lock() 5834 5835 snapst = snapstate.SnapState{} 5836 err = snapstate.Get(s.state, "some-snap", &snapst) 5837 c.Assert(err, IsNil) 5838 c.Check(snapst.IgnoreValidation, Equals, false) 5839 c.Check(snapst.Current, Equals, snap.R(11)) 5840 } 5841 5842 func (s *snapmgrTestSuite) TestParallelInstanceUpdateIgnoreValidationSticky(c *C) { 5843 si := snap.SideInfo{ 5844 RealName: "some-snap", 5845 SnapID: "some-snap-id", 5846 Revision: snap.R(7), 5847 } 5848 5849 s.state.Lock() 5850 defer s.state.Unlock() 5851 5852 tr := config.NewTransaction(s.state) 5853 tr.Set("core", "experimental.parallel-instances", true) 5854 tr.Commit() 5855 5856 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5857 Active: true, 5858 Sequence: []*snap.SideInfo{&si}, 5859 Current: si.Revision, 5860 SnapType: "app", 5861 }) 5862 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 5863 Active: true, 5864 Sequence: []*snap.SideInfo{&si}, 5865 Current: si.Revision, 5866 SnapType: "app", 5867 InstanceKey: "instance", 5868 }) 5869 5870 validateErr := errors.New("refresh control error") 5871 validateRefreshesFail := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) { 5872 c.Check(refreshes, HasLen, 2) 5873 if len(ignoreValidation) == 0 { 5874 return nil, validateErr 5875 } 5876 c.Check(ignoreValidation, DeepEquals, map[string]bool{ 5877 "some-snap_instance": true, 5878 }) 5879 return refreshes, nil 5880 } 5881 // hook it up 5882 snapstate.ValidateRefreshes = validateRefreshesFail 5883 5884 flags := snapstate.Flags{IgnoreValidation: true} 5885 ts, err := snapstate.Update(s.state, "some-snap_instance", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags) 5886 c.Assert(err, IsNil) 5887 5888 c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ 5889 op: "storesvc-snap-action", 5890 curSnaps: []store.CurrentSnap{{ 5891 InstanceName: "some-snap", 5892 SnapID: "some-snap-id", 5893 Revision: snap.R(7), 5894 IgnoreValidation: false, 5895 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 5896 Epoch: snap.E("1*"), 5897 }, { 5898 InstanceName: "some-snap_instance", 5899 SnapID: "some-snap-id", 5900 Revision: snap.R(7), 5901 IgnoreValidation: false, 5902 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 5903 Epoch: snap.E("1*"), 5904 }}, 5905 userID: 1, 5906 }) 5907 c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{ 5908 op: "storesvc-snap-action:action", 5909 revno: snap.R(11), 5910 action: store.SnapAction{ 5911 Action: "refresh", 5912 InstanceName: "some-snap_instance", 5913 SnapID: "some-snap-id", 5914 Channel: "stable", 5915 Flags: store.SnapActionIgnoreValidation, 5916 }, 5917 userID: 1, 5918 }) 5919 5920 chg := s.state.NewChange("refresh", "refresh snaps") 5921 chg.AddAll(ts) 5922 5923 s.state.Unlock() 5924 defer s.se.Stop() 5925 s.settle(c) 5926 s.state.Lock() 5927 5928 // ensure all our tasks ran 5929 c.Assert(chg.Err(), IsNil) 5930 c.Assert(chg.IsReady(), Equals, true) 5931 5932 // verify snap 'instance' has IgnoreValidation set and the snap was 5933 // updated 5934 var snapst snapstate.SnapState 5935 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 5936 c.Assert(err, IsNil) 5937 c.Check(snapst.IgnoreValidation, Equals, true) 5938 c.Check(snapst.Current, Equals, snap.R(11)) 5939 // and the other snap does not 5940 err = snapstate.Get(s.state, "some-snap", &snapst) 5941 c.Assert(err, IsNil) 5942 c.Check(snapst.Current, Equals, snap.R(7)) 5943 c.Check(snapst.IgnoreValidation, Equals, false) 5944 5945 s.fakeBackend.ops = nil 5946 s.fakeStore.refreshRevnos = map[string]snap.Revision{ 5947 "some-snap-id": snap.R(12), 5948 } 5949 updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "some-snap_instance"}, s.user.ID, nil) 5950 c.Assert(err, IsNil) 5951 c.Check(tts, HasLen, 3) 5952 verifyLastTasksetIsReRefresh(c, tts) 5953 sort.Strings(updates) 5954 c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"}) 5955 5956 chg = s.state.NewChange("refresh", "refresh snaps") 5957 for _, ts := range tts[:len(tts)-1] { 5958 chg.AddAll(ts) 5959 } 5960 5961 s.state.Unlock() 5962 s.settle(c) 5963 s.state.Lock() 5964 5965 // ensure all our tasks ran 5966 c.Assert(chg.Err(), IsNil) 5967 c.Assert(chg.IsReady(), Equals, true) 5968 5969 err = snapstate.Get(s.state, "some-snap", &snapst) 5970 c.Assert(err, IsNil) 5971 c.Check(snapst.IgnoreValidation, Equals, false) 5972 c.Check(snapst.Current, Equals, snap.R(12)) 5973 5974 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 5975 c.Assert(err, IsNil) 5976 c.Check(snapst.IgnoreValidation, Equals, true) 5977 c.Check(snapst.Current, Equals, snap.R(12)) 5978 5979 for i := 0; i < 2; i++ { 5980 op := s.fakeBackend.ops[i] 5981 switch op.op { 5982 case "storesvc-snap-action": 5983 c.Check(op, DeepEquals, fakeOp{ 5984 op: "storesvc-snap-action", 5985 curSnaps: []store.CurrentSnap{{ 5986 InstanceName: "some-snap", 5987 SnapID: "some-snap-id", 5988 Revision: snap.R(7), 5989 IgnoreValidation: false, 5990 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 5991 Epoch: snap.E("1*"), 5992 }, { 5993 InstanceName: "some-snap_instance", 5994 SnapID: "some-snap-id", 5995 Revision: snap.R(11), 5996 TrackingChannel: "stable", 5997 IgnoreValidation: true, 5998 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 11), 5999 Epoch: snap.E("1*"), 6000 }}, 6001 userID: 1, 6002 }) 6003 case "storesvc-snap-action:action": 6004 switch op.action.InstanceName { 6005 case "some-snap": 6006 c.Check(op, DeepEquals, fakeOp{ 6007 op: "storesvc-snap-action:action", 6008 revno: snap.R(12), 6009 action: store.SnapAction{ 6010 Action: "refresh", 6011 InstanceName: "some-snap", 6012 SnapID: "some-snap-id", 6013 Flags: 0, 6014 }, 6015 userID: 1, 6016 }) 6017 case "some-snap_instance": 6018 c.Check(op, DeepEquals, fakeOp{ 6019 op: "storesvc-snap-action:action", 6020 revno: snap.R(12), 6021 action: store.SnapAction{ 6022 Action: "refresh", 6023 InstanceName: "some-snap_instance", 6024 SnapID: "some-snap-id", 6025 Flags: 0, 6026 }, 6027 userID: 1, 6028 }) 6029 default: 6030 c.Fatalf("unexpected instance name %q", op.action.InstanceName) 6031 } 6032 default: 6033 c.Fatalf("unexpected action %q", op.op) 6034 } 6035 } 6036 6037 } 6038 6039 func (s *snapmgrTestSuite) TestUpdateFromLocal(c *C) { 6040 si := snap.SideInfo{ 6041 RealName: "some-snap", 6042 Revision: snap.R("x1"), 6043 } 6044 6045 s.state.Lock() 6046 defer s.state.Unlock() 6047 6048 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 6049 Active: true, 6050 Sequence: []*snap.SideInfo{&si}, 6051 Channel: "channel-for-7", 6052 Current: si.Revision, 6053 }) 6054 6055 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{}) 6056 c.Assert(err, Equals, store.ErrLocalSnap) 6057 } 6058 6059 func (s *snapmgrTestSuite) TestUpdateAmend(c *C) { 6060 si := snap.SideInfo{ 6061 RealName: "some-snap", 6062 Revision: snap.R("x1"), 6063 } 6064 6065 s.state.Lock() 6066 defer s.state.Unlock() 6067 6068 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 6069 Active: true, 6070 Sequence: []*snap.SideInfo{&si}, 6071 Channel: "channel-for-7", 6072 Current: si.Revision, 6073 }) 6074 6075 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{Amend: true}) 6076 c.Assert(err, IsNil) 6077 verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state) 6078 6079 // ensure we go from local to store revision-7 6080 var snapsup snapstate.SnapSetup 6081 tasks := ts.Tasks() 6082 c.Check(tasks[1].Kind(), Equals, "download-snap") 6083 err = tasks[1].Get("snap-setup", &snapsup) 6084 c.Assert(err, IsNil) 6085 c.Check(snapsup.Revision(), Equals, snap.R(7)) 6086 } 6087 6088 func (s *snapmgrTestSuite) TestUpdateAmendSnapNotFound(c *C) { 6089 si := snap.SideInfo{ 6090 RealName: "snap-unknown", 6091 Revision: snap.R("x1"), 6092 } 6093 6094 s.state.Lock() 6095 defer s.state.Unlock() 6096 6097 snapstate.Set(s.state, "snap-unknown", &snapstate.SnapState{ 6098 Active: true, 6099 Sequence: []*snap.SideInfo{&si}, 6100 Channel: "stable", 6101 Current: si.Revision, 6102 }) 6103 6104 _, err := snapstate.Update(s.state, "snap-unknown", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{Amend: true}) 6105 c.Assert(err, Equals, store.ErrSnapNotFound) 6106 } 6107 6108 func (s *snapmgrTestSuite) TestSingleUpdateBlockedRevision(c *C) { 6109 // single updates should *not* set the block list 6110 si7 := snap.SideInfo{ 6111 RealName: "some-snap", 6112 SnapID: "some-snap-id", 6113 Revision: snap.R(7), 6114 } 6115 si11 := snap.SideInfo{ 6116 RealName: "some-snap", 6117 SnapID: "some-snap-id", 6118 Revision: snap.R(11), 6119 } 6120 6121 s.state.Lock() 6122 defer s.state.Unlock() 6123 6124 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 6125 Active: true, 6126 Sequence: []*snap.SideInfo{&si7, &si11}, 6127 Current: si7.Revision, 6128 SnapType: "app", 6129 }) 6130 6131 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 6132 c.Assert(err, IsNil) 6133 6134 c.Assert(s.fakeBackend.ops, HasLen, 2) 6135 c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ 6136 op: "storesvc-snap-action", 6137 curSnaps: []store.CurrentSnap{{ 6138 InstanceName: "some-snap", 6139 SnapID: "some-snap-id", 6140 Revision: snap.R(7), 6141 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 6142 Epoch: snap.E("1*"), 6143 }}, 6144 userID: 1, 6145 }) 6146 } 6147 6148 func (s *snapmgrTestSuite) TestMultiUpdateBlockedRevision(c *C) { 6149 // multi-updates should *not* set the block list 6150 si7 := snap.SideInfo{ 6151 RealName: "some-snap", 6152 SnapID: "some-snap-id", 6153 Revision: snap.R(7), 6154 } 6155 si11 := snap.SideInfo{ 6156 RealName: "some-snap", 6157 SnapID: "some-snap-id", 6158 Revision: snap.R(11), 6159 } 6160 6161 s.state.Lock() 6162 defer s.state.Unlock() 6163 6164 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 6165 Active: true, 6166 Sequence: []*snap.SideInfo{&si7, &si11}, 6167 Current: si7.Revision, 6168 SnapType: "app", 6169 }) 6170 6171 updates, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil) 6172 c.Assert(err, IsNil) 6173 c.Check(updates, DeepEquals, []string{"some-snap"}) 6174 6175 c.Assert(s.fakeBackend.ops, HasLen, 2) 6176 c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ 6177 op: "storesvc-snap-action", 6178 curSnaps: []store.CurrentSnap{{ 6179 InstanceName: "some-snap", 6180 SnapID: "some-snap-id", 6181 Revision: snap.R(7), 6182 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 6183 Epoch: snap.E("1*"), 6184 }}, 6185 userID: 1, 6186 }) 6187 } 6188 6189 func (s *snapmgrTestSuite) TestAllUpdateBlockedRevision(c *C) { 6190 // update-all *should* set the block list 6191 si7 := snap.SideInfo{ 6192 RealName: "some-snap", 6193 SnapID: "some-snap-id", 6194 Revision: snap.R(7), 6195 } 6196 si11 := snap.SideInfo{ 6197 RealName: "some-snap", 6198 SnapID: "some-snap-id", 6199 Revision: snap.R(11), 6200 } 6201 6202 s.state.Lock() 6203 defer s.state.Unlock() 6204 6205 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 6206 Active: true, 6207 Sequence: []*snap.SideInfo{&si7, &si11}, 6208 Current: si7.Revision, 6209 }) 6210 6211 updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil) 6212 c.Check(err, IsNil) 6213 c.Check(updates, HasLen, 0) 6214 6215 c.Assert(s.fakeBackend.ops, HasLen, 2) 6216 c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{ 6217 op: "storesvc-snap-action", 6218 curSnaps: []store.CurrentSnap{{ 6219 InstanceName: "some-snap", 6220 SnapID: "some-snap-id", 6221 Revision: snap.R(7), 6222 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7), 6223 Block: []snap.Revision{snap.R(11)}, 6224 Epoch: snap.E("1*"), 6225 }}, 6226 userID: 1, 6227 }) 6228 } 6229 6230 var orthogonalAutoAliasesScenarios = []struct { 6231 aliasesBefore map[string][]string 6232 names []string 6233 prune []string 6234 update bool 6235 new bool 6236 }{ 6237 {nil, nil, nil, true, true}, 6238 {nil, []string{"some-snap"}, nil, true, false}, 6239 {nil, []string{"other-snap"}, nil, false, true}, 6240 {map[string][]string{"some-snap": {"aliasA", "aliasC"}}, []string{"some-snap"}, nil, true, false}, 6241 {map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, false}, 6242 {map[string][]string{"other-snap": {"aliasB", "aliasC"}}, nil, []string{"other-snap"}, true, false}, 6243 {map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"some-snap"}, nil, true, false}, 6244 {map[string][]string{"other-snap": {"aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, true}, 6245 {map[string][]string{"other-snap": {"aliasC"}}, nil, []string{"other-snap"}, true, true}, 6246 {map[string][]string{"other-snap": {"aliasC"}}, []string{"some-snap"}, nil, true, false}, 6247 {map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false}, 6248 {map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, nil, []string{"other-snap", "some-snap"}, true, true}, 6249 {map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap", "some-snap"}, false, true}, 6250 {map[string][]string{"some-snap": {"aliasB"}}, nil, []string{"some-snap"}, true, true}, 6251 {map[string][]string{"some-snap": {"aliasB"}}, []string{"other-snap"}, []string{"some-snap"}, false, true}, 6252 {map[string][]string{"some-snap": {"aliasB"}}, []string{"some-snap"}, nil, true, false}, 6253 {map[string][]string{"other-snap": {"aliasA"}}, nil, []string{"other-snap"}, true, true}, 6254 {map[string][]string{"other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap"}, false, true}, 6255 {map[string][]string{"other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false}, 6256 } 6257 6258 func (s *snapmgrTestSuite) TestUpdateManyAutoAliasesScenarios(c *C) { 6259 s.state.Lock() 6260 defer s.state.Unlock() 6261 6262 snapstate.Set(s.state, "other-snap", &snapstate.SnapState{ 6263 Active: true, 6264 Sequence: []*snap.SideInfo{ 6265 {RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)}, 6266 }, 6267 Current: snap.R(2), 6268 SnapType: "app", 6269 }) 6270 6271 snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) { 6272 switch info.InstanceName() { 6273 case "some-snap": 6274 return map[string]string{"aliasA": "cmdA"}, nil 6275 case "other-snap": 6276 return map[string]string{"aliasB": "cmdB"}, nil 6277 } 6278 return nil, nil 6279 } 6280 6281 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 6282 Active: true, 6283 Sequence: []*snap.SideInfo{ 6284 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}, 6285 }, 6286 Current: snap.R(4), 6287 SnapType: "app", 6288 }) 6289 6290 expectedSet := func(aliases []string) map[string]bool { 6291 res := make(map[string]bool, len(aliases)) 6292 for _, alias := range aliases { 6293 res[alias] = true 6294 } 6295 return res 6296 } 6297 6298 for _, scenario := range orthogonalAutoAliasesScenarios { 6299 for _, instanceName := range []string{"some-snap", "other-snap"} { 6300 var snapst snapstate.SnapState 6301 err := snapstate.Get(s.state, instanceName, &snapst) 6302 c.Assert(err, IsNil) 6303 snapst.Aliases = nil 6304 snapst.AutoAliasesDisabled = false 6305 if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil { 6306 targets := make(map[string]*snapstate.AliasTarget) 6307 for _, alias := range autoAliases { 6308 targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]} 6309 } 6310 6311 snapst.Aliases = targets 6312 } 6313 snapstate.Set(s.state, instanceName, &snapst) 6314 } 6315 6316 updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, scenario.names, s.user.ID, nil) 6317 c.Check(err, IsNil) 6318 verifyLastTasksetIsReRefresh(c, tts) 6319 6320 _, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"}) 6321 c.Assert(err, IsNil) 6322 6323 j := 0 6324 expectedUpdatesSet := make(map[string]bool) 6325 var expectedPruned map[string]map[string]bool 6326 var pruneTs *state.TaskSet 6327 if len(scenario.prune) != 0 { 6328 pruneTs = tts[0] 6329 j++ 6330 taskAliases := make(map[string]map[string]bool) 6331 for _, aliasTask := range pruneTs.Tasks() { 6332 c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases") 6333 var aliases []string 6334 err := aliasTask.Get("aliases", &aliases) 6335 c.Assert(err, IsNil) 6336 snapsup, err := snapstate.TaskSnapSetup(aliasTask) 6337 c.Assert(err, IsNil) 6338 taskAliases[snapsup.InstanceName()] = expectedSet(aliases) 6339 } 6340 expectedPruned = make(map[string]map[string]bool) 6341 for _, instanceName := range scenario.prune { 6342 expectedPruned[instanceName] = expectedSet(dropped[instanceName]) 6343 if instanceName == "other-snap" && !scenario.new && !scenario.update { 6344 expectedUpdatesSet["other-snap"] = true 6345 } 6346 } 6347 c.Check(taskAliases, DeepEquals, expectedPruned) 6348 } 6349 if scenario.update { 6350 updateTs := tts[j] 6351 j++ 6352 expectedUpdatesSet["some-snap"] = true 6353 first := updateTs.Tasks()[0] 6354 c.Check(first.Kind(), Equals, "prerequisites") 6355 wait := false 6356 if expectedPruned["other-snap"]["aliasA"] { 6357 wait = true 6358 } else if expectedPruned["some-snap"] != nil { 6359 wait = true 6360 } 6361 if wait { 6362 c.Check(first.WaitTasks(), DeepEquals, pruneTs.Tasks()) 6363 } else { 6364 c.Check(first.WaitTasks(), HasLen, 0) 6365 } 6366 } 6367 if scenario.new { 6368 newTs := tts[j] 6369 j++ 6370 expectedUpdatesSet["other-snap"] = true 6371 tasks := newTs.Tasks() 6372 c.Check(tasks, HasLen, 1) 6373 aliasTask := tasks[0] 6374 c.Check(aliasTask.Kind(), Equals, "refresh-aliases") 6375 6376 wait := false 6377 if expectedPruned["some-snap"]["aliasB"] { 6378 wait = true 6379 } else if expectedPruned["other-snap"] != nil { 6380 wait = true 6381 } 6382 if wait { 6383 c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTs.Tasks()) 6384 } else { 6385 c.Check(aliasTask.WaitTasks(), HasLen, 0) 6386 } 6387 } 6388 c.Assert(j, Equals, len(tts)-1, Commentf("%#v", scenario)) 6389 6390 // check reported updated names 6391 c.Check(len(updates) > 0, Equals, true) 6392 sort.Strings(updates) 6393 expectedUpdates := make([]string, 0, len(expectedUpdatesSet)) 6394 for x := range expectedUpdatesSet { 6395 expectedUpdates = append(expectedUpdates, x) 6396 } 6397 sort.Strings(expectedUpdates) 6398 c.Check(updates, DeepEquals, expectedUpdates) 6399 } 6400 } 6401 6402 func (s *snapmgrTestSuite) TestUpdateOneAutoAliasesScenarios(c *C) { 6403 s.state.Lock() 6404 defer s.state.Unlock() 6405 6406 snapstate.Set(s.state, "other-snap", &snapstate.SnapState{ 6407 Active: true, 6408 Sequence: []*snap.SideInfo{ 6409 {RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)}, 6410 }, 6411 Current: snap.R(2), 6412 SnapType: "app", 6413 }) 6414 6415 snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) { 6416 switch info.InstanceName() { 6417 case "some-snap": 6418 return map[string]string{"aliasA": "cmdA"}, nil 6419 case "other-snap": 6420 return map[string]string{"aliasB": "cmdB"}, nil 6421 } 6422 return nil, nil 6423 } 6424 6425 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 6426 Active: true, 6427 Sequence: []*snap.SideInfo{ 6428 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}, 6429 }, 6430 Current: snap.R(4), 6431 SnapType: "app", 6432 }) 6433 6434 expectedSet := func(aliases []string) map[string]bool { 6435 res := make(map[string]bool, len(aliases)) 6436 for _, alias := range aliases { 6437 res[alias] = true 6438 } 6439 return res 6440 } 6441 6442 for _, scenario := range orthogonalAutoAliasesScenarios { 6443 if len(scenario.names) != 1 { 6444 continue 6445 } 6446 6447 for _, instanceName := range []string{"some-snap", "other-snap"} { 6448 var snapst snapstate.SnapState 6449 err := snapstate.Get(s.state, instanceName, &snapst) 6450 c.Assert(err, IsNil) 6451 snapst.Aliases = nil 6452 snapst.AutoAliasesDisabled = false 6453 if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil { 6454 targets := make(map[string]*snapstate.AliasTarget) 6455 for _, alias := range autoAliases { 6456 targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]} 6457 } 6458 6459 snapst.Aliases = targets 6460 } 6461 snapstate.Set(s.state, instanceName, &snapst) 6462 } 6463 6464 ts, err := snapstate.Update(s.state, scenario.names[0], nil, s.user.ID, snapstate.Flags{}) 6465 c.Assert(err, IsNil) 6466 _, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"}) 6467 c.Assert(err, IsNil) 6468 6469 j := 0 6470 6471 tasks := ts.Tasks() 6472 // make sure the last task from Update is the rerefresh 6473 c.Assert(tasks[len(tasks)-1].Kind(), Equals, "check-rerefresh") 6474 tasks = tasks[:len(tasks)-1] // and now forget about it 6475 6476 var expectedPruned map[string]map[string]bool 6477 var pruneTasks []*state.Task 6478 if len(scenario.prune) != 0 { 6479 nprune := len(scenario.prune) 6480 pruneTasks = tasks[:nprune] 6481 j += nprune 6482 taskAliases := make(map[string]map[string]bool) 6483 for _, aliasTask := range pruneTasks { 6484 c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases") 6485 var aliases []string 6486 err := aliasTask.Get("aliases", &aliases) 6487 c.Assert(err, IsNil) 6488 snapsup, err := snapstate.TaskSnapSetup(aliasTask) 6489 c.Assert(err, IsNil) 6490 taskAliases[snapsup.InstanceName()] = expectedSet(aliases) 6491 } 6492 expectedPruned = make(map[string]map[string]bool) 6493 for _, instanceName := range scenario.prune { 6494 expectedPruned[instanceName] = expectedSet(dropped[instanceName]) 6495 } 6496 c.Check(taskAliases, DeepEquals, expectedPruned) 6497 } 6498 if scenario.update { 6499 first := tasks[j] 6500 j += 19 6501 c.Check(first.Kind(), Equals, "prerequisites") 6502 wait := false 6503 if expectedPruned["other-snap"]["aliasA"] { 6504 wait = true 6505 } else if expectedPruned["some-snap"] != nil { 6506 wait = true 6507 } 6508 if wait { 6509 c.Check(first.WaitTasks(), DeepEquals, pruneTasks) 6510 } else { 6511 c.Check(first.WaitTasks(), HasLen, 0) 6512 } 6513 } 6514 if scenario.new { 6515 aliasTask := tasks[j] 6516 j++ 6517 c.Check(aliasTask.Kind(), Equals, "refresh-aliases") 6518 wait := false 6519 if expectedPruned["some-snap"]["aliasB"] { 6520 wait = true 6521 } else if expectedPruned["other-snap"] != nil { 6522 wait = true 6523 } 6524 if wait { 6525 c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTasks) 6526 } else { 6527 c.Check(aliasTask.WaitTasks(), HasLen, 0) 6528 } 6529 } 6530 c.Assert(len(tasks), Equals, j, Commentf("%#v", scenario)) 6531 6532 // conflict checks are triggered 6533 chg := s.state.NewChange("update", "...") 6534 chg.AddAll(ts) 6535 err = snapstate.CheckChangeConflict(s.state, scenario.names[0], nil) 6536 c.Check(err, ErrorMatches, `.* has "update" change in progress`) 6537 chg.SetStatus(state.DoneStatus) 6538 } 6539 } 6540 6541 func (s *snapmgrTestSuite) TestUpdateLocalSnapFails(c *C) { 6542 si := snap.SideInfo{ 6543 RealName: "some-snap", 6544 Revision: snap.R(7), 6545 } 6546 6547 s.state.Lock() 6548 defer s.state.Unlock() 6549 6550 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 6551 Active: true, 6552 Sequence: []*snap.SideInfo{&si}, 6553 Current: si.Revision, 6554 }) 6555 6556 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 6557 c.Assert(err, Equals, store.ErrLocalSnap) 6558 } 6559 6560 func (s *snapmgrTestSuite) TestUpdateDisabledUnsupported(c *C) { 6561 si := snap.SideInfo{ 6562 RealName: "some-snap", 6563 SnapID: "some-snap-id", 6564 Revision: snap.R(7), 6565 } 6566 6567 s.state.Lock() 6568 defer s.state.Unlock() 6569 6570 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 6571 Active: false, 6572 Sequence: []*snap.SideInfo{&si}, 6573 Current: si.Revision, 6574 }) 6575 6576 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 6577 c.Assert(err, ErrorMatches, `refreshing disabled snap "some-snap" not supported`) 6578 } 6579 6580 func (s *snapmgrTestSuite) TestUpdateKernelTrackChecksSwitchingTracks(c *C) { 6581 si := snap.SideInfo{ 6582 RealName: "kernel", 6583 SnapID: "kernel-id", 6584 Revision: snap.R(7), 6585 } 6586 6587 s.state.Lock() 6588 defer s.state.Unlock() 6589 6590 r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 6591 defer r() 6592 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 6593 Active: true, 6594 Sequence: []*snap.SideInfo{&si}, 6595 Current: si.Revision, 6596 Channel: "18/stable", 6597 }) 6598 6599 // switching tracks is not ok 6600 _, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{}) 6601 c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`) 6602 6603 // no change to the channel is ok 6604 _, err = snapstate.Update(s.state, "kernel", nil, s.user.ID, snapstate.Flags{}) 6605 c.Assert(err, IsNil) 6606 6607 // switching risk level is ok 6608 _, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{}) 6609 c.Assert(err, IsNil) 6610 6611 // switching just risk within the pinned track is ok 6612 _, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{}) 6613 c.Assert(err, IsNil) 6614 } 6615 6616 func (s *snapmgrTestSuite) TestUpdateGadgetTrackChecksSwitchingTracks(c *C) { 6617 si := snap.SideInfo{ 6618 RealName: "brand-gadget", 6619 SnapID: "brand-gadget-id", 6620 Revision: snap.R(7), 6621 } 6622 6623 s.state.Lock() 6624 defer s.state.Unlock() 6625 6626 r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18")) 6627 defer r() 6628 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 6629 Active: true, 6630 Sequence: []*snap.SideInfo{&si}, 6631 Current: si.Revision, 6632 Channel: "18/stable", 6633 }) 6634 6635 // switching tracks is not ok 6636 _, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{}) 6637 c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`) 6638 6639 // no change to the channel is ok 6640 _, err = snapstate.Update(s.state, "brand-gadget", nil, s.user.ID, snapstate.Flags{}) 6641 c.Assert(err, IsNil) 6642 6643 // switching risk level is ok 6644 _, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{}) 6645 c.Assert(err, IsNil) 6646 6647 // switching just risk within the pinned track is ok 6648 _, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{}) 6649 c.Assert(err, IsNil) 6650 6651 } 6652 6653 func makeTestSnap(c *C, snapYamlContent string) (snapFilePath string) { 6654 return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, nil) 6655 } 6656 6657 func (s *snapmgrTestSuite) TestInstallFirstLocalRunThrough(c *C) { 6658 // use the real thing for this one 6659 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 6660 6661 s.state.Lock() 6662 defer s.state.Unlock() 6663 6664 mockSnap := makeTestSnap(c, `name: mock 6665 version: 1.0`) 6666 chg := s.state.NewChange("install", "install a local snap") 6667 ts, info, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{}) 6668 c.Assert(err, IsNil) 6669 chg.AddAll(ts) 6670 6671 // ensure the returned info is correct 6672 c.Check(info.SideInfo.RealName, Equals, "mock") 6673 c.Check(info.Version, Equals, "1.0") 6674 6675 s.state.Unlock() 6676 defer s.se.Stop() 6677 s.settle(c) 6678 s.state.Lock() 6679 6680 expected := fakeOps{ 6681 { 6682 // only local install was run, i.e. first actions are pseudo-action current 6683 op: "current", 6684 old: "<no-current>", 6685 }, 6686 { 6687 // and setup-snap 6688 op: "setup-snap", 6689 name: "mock", 6690 path: mockSnap, 6691 revno: snap.R("x1"), 6692 }, 6693 { 6694 op: "copy-data", 6695 path: filepath.Join(dirs.SnapMountDir, "mock/x1"), 6696 old: "<no-old>", 6697 }, 6698 { 6699 op: "setup-profiles:Doing", 6700 name: "mock", 6701 revno: snap.R("x1"), 6702 }, 6703 { 6704 op: "candidate", 6705 sinfo: snap.SideInfo{ 6706 RealName: "mock", 6707 Revision: snap.R("x1"), 6708 }, 6709 }, 6710 { 6711 op: "link-snap", 6712 path: filepath.Join(dirs.SnapMountDir, "mock/x1"), 6713 }, 6714 { 6715 op: "auto-connect:Doing", 6716 name: "mock", 6717 revno: snap.R("x1"), 6718 }, 6719 { 6720 op: "update-aliases", 6721 }, 6722 { 6723 op: "cleanup-trash", 6724 name: "mock", 6725 revno: snap.R("x1"), 6726 }, 6727 } 6728 6729 // start with an easier-to-read error if this fails: 6730 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 6731 c.Check(s.fakeBackend.ops, DeepEquals, expected) 6732 6733 // verify snapSetup info 6734 var snapsup snapstate.SnapSetup 6735 task := ts.Tasks()[1] 6736 err = task.Get("snap-setup", &snapsup) 6737 c.Assert(err, IsNil) 6738 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 6739 SnapPath: mockSnap, 6740 SideInfo: snapsup.SideInfo, 6741 Type: snap.TypeApp, 6742 PlugsOnly: true, 6743 }) 6744 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 6745 RealName: "mock", 6746 Revision: snap.R(-1), 6747 }) 6748 6749 // verify snaps in the system state 6750 var snapst snapstate.SnapState 6751 err = snapstate.Get(s.state, "mock", &snapst) 6752 c.Assert(err, IsNil) 6753 6754 c.Assert(snapst.Active, Equals, true) 6755 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 6756 RealName: "mock", 6757 Channel: "", 6758 Revision: snap.R(-1), 6759 }) 6760 c.Assert(snapst.LocalRevision(), Equals, snap.R(-1)) 6761 } 6762 6763 func (s *snapmgrTestSuite) TestInstallSubsequentLocalRunThrough(c *C) { 6764 // use the real thing for this one 6765 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 6766 6767 s.state.Lock() 6768 defer s.state.Unlock() 6769 6770 snapstate.Set(s.state, "mock", &snapstate.SnapState{ 6771 Active: true, 6772 Sequence: []*snap.SideInfo{ 6773 {RealName: "mock", Revision: snap.R(-2)}, 6774 }, 6775 Current: snap.R(-2), 6776 SnapType: "app", 6777 }) 6778 6779 mockSnap := makeTestSnap(c, `name: mock 6780 version: 1.0 6781 epoch: 1* 6782 `) 6783 chg := s.state.NewChange("install", "install a local snap") 6784 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{}) 6785 c.Assert(err, IsNil) 6786 chg.AddAll(ts) 6787 6788 s.state.Unlock() 6789 defer s.se.Stop() 6790 s.settle(c) 6791 s.state.Lock() 6792 6793 expected := fakeOps{ 6794 { 6795 op: "current", 6796 old: filepath.Join(dirs.SnapMountDir, "mock/x2"), 6797 }, 6798 { 6799 op: "setup-snap", 6800 name: "mock", 6801 path: mockSnap, 6802 revno: snap.R("x3"), 6803 }, 6804 { 6805 op: "remove-snap-aliases", 6806 name: "mock", 6807 }, 6808 { 6809 op: "unlink-snap", 6810 path: filepath.Join(dirs.SnapMountDir, "mock/x2"), 6811 }, 6812 { 6813 op: "copy-data", 6814 path: filepath.Join(dirs.SnapMountDir, "mock/x3"), 6815 old: filepath.Join(dirs.SnapMountDir, "mock/x2"), 6816 }, 6817 { 6818 op: "setup-profiles:Doing", 6819 name: "mock", 6820 revno: snap.R(-3), 6821 }, 6822 { 6823 op: "candidate", 6824 sinfo: snap.SideInfo{ 6825 RealName: "mock", 6826 Revision: snap.R(-3), 6827 }, 6828 }, 6829 { 6830 op: "link-snap", 6831 path: filepath.Join(dirs.SnapMountDir, "mock/x3"), 6832 }, 6833 { 6834 op: "auto-connect:Doing", 6835 name: "mock", 6836 revno: snap.R("x3"), 6837 }, 6838 { 6839 op: "update-aliases", 6840 }, 6841 { 6842 op: "cleanup-trash", 6843 name: "mock", 6844 revno: snap.R("x3"), 6845 }, 6846 } 6847 6848 // start with an easier-to-read error if this fails: 6849 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 6850 c.Check(s.fakeBackend.ops, DeepEquals, expected) 6851 6852 // verify snapSetup info 6853 var snapsup snapstate.SnapSetup 6854 task := ts.Tasks()[1] 6855 err = task.Get("snap-setup", &snapsup) 6856 c.Assert(err, IsNil) 6857 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 6858 SnapPath: mockSnap, 6859 SideInfo: snapsup.SideInfo, 6860 Type: snap.TypeApp, 6861 PlugsOnly: true, 6862 }) 6863 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 6864 RealName: "mock", 6865 Revision: snap.R(-3), 6866 }) 6867 6868 // verify snaps in the system state 6869 var snapst snapstate.SnapState 6870 err = snapstate.Get(s.state, "mock", &snapst) 6871 c.Assert(err, IsNil) 6872 6873 c.Assert(snapst.Active, Equals, true) 6874 c.Assert(snapst.Sequence, HasLen, 2) 6875 c.Assert(snapst.CurrentSideInfo(), DeepEquals, &snap.SideInfo{ 6876 RealName: "mock", 6877 Channel: "", 6878 Revision: snap.R(-3), 6879 }) 6880 c.Assert(snapst.LocalRevision(), Equals, snap.R(-3)) 6881 } 6882 6883 func (s *snapmgrTestSuite) TestInstallOldSubsequentLocalRunThrough(c *C) { 6884 // use the real thing for this one 6885 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 6886 6887 s.state.Lock() 6888 defer s.state.Unlock() 6889 6890 snapstate.Set(s.state, "mock", &snapstate.SnapState{ 6891 Active: true, 6892 Sequence: []*snap.SideInfo{ 6893 {RealName: "mock", Revision: snap.R(100001)}, 6894 }, 6895 Current: snap.R(100001), 6896 SnapType: "app", 6897 }) 6898 6899 mockSnap := makeTestSnap(c, `name: mock 6900 version: 1.0 6901 epoch: 1* 6902 `) 6903 chg := s.state.NewChange("install", "install a local snap") 6904 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{}) 6905 c.Assert(err, IsNil) 6906 chg.AddAll(ts) 6907 6908 s.state.Unlock() 6909 defer s.se.Stop() 6910 s.settle(c) 6911 s.state.Lock() 6912 6913 expected := fakeOps{ 6914 { 6915 // ensure only local install was run, i.e. first action is pseudo-action current 6916 op: "current", 6917 old: filepath.Join(dirs.SnapMountDir, "mock/100001"), 6918 }, 6919 { 6920 // and setup-snap 6921 op: "setup-snap", 6922 name: "mock", 6923 path: mockSnap, 6924 revno: snap.R("x1"), 6925 }, 6926 { 6927 op: "remove-snap-aliases", 6928 name: "mock", 6929 }, 6930 { 6931 op: "unlink-snap", 6932 path: filepath.Join(dirs.SnapMountDir, "mock/100001"), 6933 }, 6934 { 6935 op: "copy-data", 6936 path: filepath.Join(dirs.SnapMountDir, "mock/x1"), 6937 old: filepath.Join(dirs.SnapMountDir, "mock/100001"), 6938 }, 6939 { 6940 op: "setup-profiles:Doing", 6941 name: "mock", 6942 revno: snap.R("x1"), 6943 }, 6944 { 6945 op: "candidate", 6946 sinfo: snap.SideInfo{ 6947 RealName: "mock", 6948 Revision: snap.R("x1"), 6949 }, 6950 }, 6951 { 6952 op: "link-snap", 6953 path: filepath.Join(dirs.SnapMountDir, "mock/x1"), 6954 }, 6955 { 6956 op: "auto-connect:Doing", 6957 name: "mock", 6958 revno: snap.R("x1"), 6959 }, 6960 { 6961 op: "update-aliases", 6962 }, 6963 { 6964 // and cleanup 6965 op: "cleanup-trash", 6966 name: "mock", 6967 revno: snap.R("x1"), 6968 }, 6969 } 6970 // start with an easier-to-read error if this fails: 6971 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 6972 c.Check(s.fakeBackend.ops, DeepEquals, expected) 6973 6974 var snapst snapstate.SnapState 6975 err = snapstate.Get(s.state, "mock", &snapst) 6976 c.Assert(err, IsNil) 6977 6978 c.Assert(snapst.Active, Equals, true) 6979 c.Assert(snapst.Sequence, HasLen, 2) 6980 c.Assert(snapst.CurrentSideInfo(), DeepEquals, &snap.SideInfo{ 6981 RealName: "mock", 6982 Channel: "", 6983 Revision: snap.R(-1), 6984 }) 6985 c.Assert(snapst.LocalRevision(), Equals, snap.R(-1)) 6986 } 6987 6988 func (s *snapmgrTestSuite) TestInstallPathWithMetadataRunThrough(c *C) { 6989 // use the real thing for this one 6990 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 6991 6992 s.state.Lock() 6993 defer s.state.Unlock() 6994 6995 someSnap := makeTestSnap(c, `name: orig-name 6996 version: 1.0`) 6997 chg := s.state.NewChange("install", "install a local snap") 6998 6999 si := &snap.SideInfo{ 7000 RealName: "some-snap", 7001 SnapID: "some-snap-id", 7002 Revision: snap.R(42), 7003 } 7004 ts, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "", snapstate.Flags{Required: true}) 7005 c.Assert(err, IsNil) 7006 chg.AddAll(ts) 7007 7008 s.state.Unlock() 7009 defer s.se.Stop() 7010 s.settle(c) 7011 s.state.Lock() 7012 7013 // ensure only local install was run, i.e. first actions are pseudo-action current 7014 c.Assert(s.fakeBackend.ops.Ops(), HasLen, 9) 7015 c.Check(s.fakeBackend.ops[0].op, Equals, "current") 7016 c.Check(s.fakeBackend.ops[0].old, Equals, "<no-current>") 7017 // and setup-snap 7018 c.Check(s.fakeBackend.ops[1].op, Equals, "setup-snap") 7019 c.Check(s.fakeBackend.ops[1].name, Equals, "some-snap") 7020 c.Check(s.fakeBackend.ops[1].path, Matches, `.*/orig-name_1.0_all.snap`) 7021 c.Check(s.fakeBackend.ops[1].revno, Equals, snap.R(42)) 7022 7023 c.Check(s.fakeBackend.ops[4].op, Equals, "candidate") 7024 c.Check(s.fakeBackend.ops[4].sinfo, DeepEquals, *si) 7025 c.Check(s.fakeBackend.ops[5].op, Equals, "link-snap") 7026 c.Check(s.fakeBackend.ops[5].path, Equals, filepath.Join(dirs.SnapMountDir, "some-snap/42")) 7027 7028 // verify snapSetup info 7029 var snapsup snapstate.SnapSetup 7030 task := ts.Tasks()[0] 7031 err = task.Get("snap-setup", &snapsup) 7032 c.Assert(err, IsNil) 7033 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 7034 SnapPath: someSnap, 7035 SideInfo: snapsup.SideInfo, 7036 Flags: snapstate.Flags{ 7037 Required: true, 7038 }, 7039 Type: snap.TypeApp, 7040 PlugsOnly: true, 7041 }) 7042 c.Assert(snapsup.SideInfo, DeepEquals, si) 7043 7044 // verify snaps in the system state 7045 var snapst snapstate.SnapState 7046 err = snapstate.Get(s.state, "some-snap", &snapst) 7047 c.Assert(err, IsNil) 7048 7049 c.Assert(snapst.Active, Equals, true) 7050 c.Assert(snapst.Channel, Equals, "") 7051 c.Assert(snapst.Sequence[0], DeepEquals, si) 7052 c.Assert(snapst.LocalRevision().Unset(), Equals, true) 7053 c.Assert(snapst.Required, Equals, true) 7054 } 7055 7056 func (s *snapmgrTestSuite) TestRemoveRunThrough(c *C) { 7057 c.Assert(snapstate.KeepAuxStoreInfo("some-snap-id", nil), IsNil) 7058 c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FilePresent) 7059 si := snap.SideInfo{ 7060 SnapID: "some-snap-id", 7061 RealName: "some-snap", 7062 Revision: snap.R(7), 7063 } 7064 7065 s.state.Lock() 7066 defer s.state.Unlock() 7067 7068 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7069 Active: true, 7070 Sequence: []*snap.SideInfo{&si}, 7071 Current: si.Revision, 7072 SnapType: "app", 7073 }) 7074 7075 chg := s.state.NewChange("remove", "remove a snap") 7076 ts, err := snapstate.Remove(s.state, "some-snap", snap.R(0), nil) 7077 c.Assert(err, IsNil) 7078 chg.AddAll(ts) 7079 7080 s.state.Unlock() 7081 defer s.se.Stop() 7082 s.settle(c) 7083 s.state.Lock() 7084 7085 expected := fakeOps{ 7086 { 7087 op: "auto-disconnect:Doing", 7088 name: "some-snap", 7089 revno: snap.R(7), 7090 }, 7091 { 7092 op: "remove-snap-aliases", 7093 name: "some-snap", 7094 }, 7095 { 7096 op: "unlink-snap", 7097 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 7098 }, 7099 { 7100 op: "remove-profiles:Doing", 7101 name: "some-snap", 7102 revno: snap.R(7), 7103 }, 7104 { 7105 op: "remove-snap-data", 7106 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 7107 }, 7108 { 7109 op: "remove-snap-common-data", 7110 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 7111 }, 7112 { 7113 op: "remove-snap-data-dir", 7114 name: "some-snap", 7115 path: filepath.Join(dirs.SnapDataDir, "some-snap"), 7116 }, 7117 { 7118 op: "remove-snap-files", 7119 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 7120 stype: "app", 7121 }, 7122 { 7123 op: "discard-namespace", 7124 name: "some-snap", 7125 }, 7126 { 7127 op: "remove-snap-dir", 7128 name: "some-snap", 7129 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 7130 }, 7131 } 7132 // start with an easier-to-read error if this fails: 7133 c.Check(len(s.fakeBackend.ops), Equals, len(expected)) 7134 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 7135 c.Check(s.fakeBackend.ops, DeepEquals, expected) 7136 7137 // verify snapSetup info 7138 tasks := ts.Tasks() 7139 for _, t := range tasks { 7140 if t.Kind() == "run-hook" { 7141 continue 7142 } 7143 if t.Kind() == "save-snapshot" { 7144 continue 7145 } 7146 snapsup, err := snapstate.TaskSnapSetup(t) 7147 c.Assert(err, IsNil) 7148 7149 var expSnapSetup *snapstate.SnapSetup 7150 switch t.Kind() { 7151 case "discard-conns": 7152 expSnapSetup = &snapstate.SnapSetup{ 7153 SideInfo: &snap.SideInfo{ 7154 RealName: "some-snap", 7155 }, 7156 } 7157 case "clear-snap", "discard-snap": 7158 expSnapSetup = &snapstate.SnapSetup{ 7159 SideInfo: &snap.SideInfo{ 7160 RealName: "some-snap", 7161 SnapID: "some-snap-id", 7162 Revision: snap.R(7), 7163 }, 7164 } 7165 default: 7166 expSnapSetup = &snapstate.SnapSetup{ 7167 SideInfo: &snap.SideInfo{ 7168 RealName: "some-snap", 7169 Revision: snap.R(7), 7170 SnapID: "some-snap-id", 7171 }, 7172 Type: snap.TypeApp, 7173 PlugsOnly: true, 7174 } 7175 7176 } 7177 7178 c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind())) 7179 } 7180 7181 // verify snaps in the system state 7182 var snapst snapstate.SnapState 7183 err = snapstate.Get(s.state, "some-snap", &snapst) 7184 c.Assert(err, Equals, state.ErrNoState) 7185 c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FileAbsent) 7186 7187 } 7188 7189 func (s *snapmgrTestSuite) TestParallelInstanceRemoveRunThrough(c *C) { 7190 si := snap.SideInfo{ 7191 RealName: "some-snap", 7192 Revision: snap.R(7), 7193 } 7194 7195 s.state.Lock() 7196 defer s.state.Unlock() 7197 7198 // pretend we have both a regular snap and a parallel instance 7199 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 7200 Active: true, 7201 Sequence: []*snap.SideInfo{&si}, 7202 Current: si.Revision, 7203 SnapType: "app", 7204 InstanceKey: "instance", 7205 }) 7206 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7207 Active: true, 7208 Sequence: []*snap.SideInfo{&si}, 7209 Current: si.Revision, 7210 SnapType: "app", 7211 }) 7212 7213 chg := s.state.NewChange("remove", "remove a snap") 7214 ts, err := snapstate.Remove(s.state, "some-snap_instance", snap.R(0), nil) 7215 c.Assert(err, IsNil) 7216 chg.AddAll(ts) 7217 7218 s.state.Unlock() 7219 s.settle(c) 7220 s.state.Lock() 7221 7222 expected := fakeOps{ 7223 { 7224 op: "auto-disconnect:Doing", 7225 name: "some-snap_instance", 7226 revno: snap.R(7), 7227 }, 7228 { 7229 op: "remove-snap-aliases", 7230 name: "some-snap_instance", 7231 }, 7232 { 7233 op: "unlink-snap", 7234 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 7235 }, 7236 { 7237 op: "remove-profiles:Doing", 7238 name: "some-snap_instance", 7239 revno: snap.R(7), 7240 }, 7241 { 7242 op: "remove-snap-data", 7243 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 7244 }, 7245 { 7246 op: "remove-snap-common-data", 7247 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 7248 }, 7249 { 7250 op: "remove-snap-data-dir", 7251 name: "some-snap_instance", 7252 path: filepath.Join(dirs.SnapDataDir, "some-snap"), 7253 otherInstances: true, 7254 }, 7255 { 7256 op: "remove-snap-files", 7257 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 7258 stype: "app", 7259 }, 7260 { 7261 op: "discard-namespace", 7262 name: "some-snap_instance", 7263 }, 7264 { 7265 op: "remove-snap-dir", 7266 name: "some-snap_instance", 7267 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 7268 otherInstances: true, 7269 }, 7270 } 7271 // start with an easier-to-read error if this fails: 7272 c.Check(len(s.fakeBackend.ops), Equals, len(expected)) 7273 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 7274 c.Check(s.fakeBackend.ops, DeepEquals, expected) 7275 7276 // verify snapSetup info 7277 tasks := ts.Tasks() 7278 for _, t := range tasks { 7279 if t.Kind() == "run-hook" { 7280 continue 7281 } 7282 if t.Kind() == "save-snapshot" { 7283 continue 7284 } 7285 snapsup, err := snapstate.TaskSnapSetup(t) 7286 c.Assert(err, IsNil) 7287 7288 var expSnapSetup *snapstate.SnapSetup 7289 switch t.Kind() { 7290 case "discard-conns": 7291 expSnapSetup = &snapstate.SnapSetup{ 7292 SideInfo: &snap.SideInfo{ 7293 RealName: "some-snap", 7294 }, 7295 InstanceKey: "instance", 7296 } 7297 case "clear-snap", "discard-snap": 7298 expSnapSetup = &snapstate.SnapSetup{ 7299 SideInfo: &snap.SideInfo{ 7300 RealName: "some-snap", 7301 Revision: snap.R(7), 7302 }, 7303 InstanceKey: "instance", 7304 } 7305 default: 7306 expSnapSetup = &snapstate.SnapSetup{ 7307 SideInfo: &snap.SideInfo{ 7308 RealName: "some-snap", 7309 Revision: snap.R(7), 7310 }, 7311 Type: snap.TypeApp, 7312 PlugsOnly: true, 7313 InstanceKey: "instance", 7314 } 7315 7316 } 7317 7318 c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind())) 7319 } 7320 7321 // verify snaps in the system state 7322 var snapst snapstate.SnapState 7323 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 7324 c.Assert(err, Equals, state.ErrNoState) 7325 7326 // the non-instance snap is still there 7327 err = snapstate.Get(s.state, "some-snap", &snapst) 7328 c.Assert(err, IsNil) 7329 } 7330 7331 func (s *snapmgrTestSuite) TestParallelInstanceRemoveRunThroughOtherInstances(c *C) { 7332 si := snap.SideInfo{ 7333 RealName: "some-snap", 7334 Revision: snap.R(7), 7335 } 7336 7337 s.state.Lock() 7338 defer s.state.Unlock() 7339 7340 // pretend we have both a regular snap and a parallel instance 7341 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 7342 Active: true, 7343 Sequence: []*snap.SideInfo{&si}, 7344 Current: si.Revision, 7345 SnapType: "app", 7346 InstanceKey: "instance", 7347 }) 7348 snapstate.Set(s.state, "some-snap_other", &snapstate.SnapState{ 7349 Active: true, 7350 Sequence: []*snap.SideInfo{&si}, 7351 Current: si.Revision, 7352 SnapType: "app", 7353 InstanceKey: "other", 7354 }) 7355 7356 chg := s.state.NewChange("remove", "remove a snap") 7357 ts, err := snapstate.Remove(s.state, "some-snap_instance", snap.R(0), nil) 7358 c.Assert(err, IsNil) 7359 chg.AddAll(ts) 7360 7361 s.state.Unlock() 7362 s.settle(c) 7363 s.state.Lock() 7364 7365 expected := fakeOps{ 7366 { 7367 op: "auto-disconnect:Doing", 7368 name: "some-snap_instance", 7369 revno: snap.R(7), 7370 }, 7371 { 7372 op: "remove-snap-aliases", 7373 name: "some-snap_instance", 7374 }, 7375 { 7376 op: "unlink-snap", 7377 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 7378 }, 7379 { 7380 op: "remove-profiles:Doing", 7381 name: "some-snap_instance", 7382 revno: snap.R(7), 7383 }, 7384 { 7385 op: "remove-snap-data", 7386 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 7387 }, 7388 { 7389 op: "remove-snap-common-data", 7390 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 7391 }, 7392 { 7393 op: "remove-snap-data-dir", 7394 name: "some-snap_instance", 7395 path: filepath.Join(dirs.SnapDataDir, "some-snap"), 7396 otherInstances: true, 7397 }, 7398 { 7399 op: "remove-snap-files", 7400 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 7401 stype: "app", 7402 }, 7403 { 7404 op: "discard-namespace", 7405 name: "some-snap_instance", 7406 }, 7407 { 7408 op: "remove-snap-dir", 7409 name: "some-snap_instance", 7410 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 7411 otherInstances: true, 7412 }, 7413 } 7414 // start with an easier-to-read error if this fails: 7415 c.Check(len(s.fakeBackend.ops), Equals, len(expected)) 7416 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 7417 c.Check(s.fakeBackend.ops, DeepEquals, expected) 7418 7419 // verify snaps in the system state 7420 var snapst snapstate.SnapState 7421 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 7422 c.Assert(err, Equals, state.ErrNoState) 7423 7424 // the other instance is still there 7425 err = snapstate.Get(s.state, "some-snap_other", &snapst) 7426 c.Assert(err, IsNil) 7427 } 7428 7429 func (s *snapmgrTestSuite) TestRemoveWithManyRevisionsRunThrough(c *C) { 7430 si3 := snap.SideInfo{ 7431 SnapID: "some-snap-id", 7432 RealName: "some-snap", 7433 Revision: snap.R(3), 7434 } 7435 7436 si5 := snap.SideInfo{ 7437 SnapID: "some-snap-id", 7438 RealName: "some-snap", 7439 Revision: snap.R(5), 7440 } 7441 7442 si7 := snap.SideInfo{ 7443 SnapID: "some-snap-id", 7444 RealName: "some-snap", 7445 Revision: snap.R(7), 7446 } 7447 7448 s.state.Lock() 7449 defer s.state.Unlock() 7450 7451 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7452 Active: true, 7453 Sequence: []*snap.SideInfo{&si5, &si3, &si7}, 7454 Current: si7.Revision, 7455 SnapType: "app", 7456 }) 7457 7458 chg := s.state.NewChange("remove", "remove a snap") 7459 ts, err := snapstate.Remove(s.state, "some-snap", snap.R(0), nil) 7460 c.Assert(err, IsNil) 7461 chg.AddAll(ts) 7462 7463 s.state.Unlock() 7464 defer s.se.Stop() 7465 s.settle(c) 7466 s.state.Lock() 7467 7468 expected := fakeOps{ 7469 { 7470 op: "auto-disconnect:Doing", 7471 name: "some-snap", 7472 revno: snap.R(7), 7473 }, 7474 { 7475 op: "remove-snap-aliases", 7476 name: "some-snap", 7477 }, 7478 { 7479 op: "unlink-snap", 7480 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 7481 }, 7482 { 7483 op: "remove-profiles:Doing", 7484 name: "some-snap", 7485 revno: snap.R(7), 7486 }, 7487 { 7488 op: "remove-snap-data", 7489 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 7490 }, 7491 { 7492 op: "remove-snap-files", 7493 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 7494 stype: "app", 7495 }, 7496 { 7497 op: "remove-snap-data", 7498 path: filepath.Join(dirs.SnapMountDir, "some-snap/3"), 7499 }, 7500 { 7501 op: "remove-snap-files", 7502 path: filepath.Join(dirs.SnapMountDir, "some-snap/3"), 7503 stype: "app", 7504 }, 7505 { 7506 op: "remove-snap-data", 7507 path: filepath.Join(dirs.SnapMountDir, "some-snap/5"), 7508 }, 7509 { 7510 op: "remove-snap-common-data", 7511 path: filepath.Join(dirs.SnapMountDir, "some-snap/5"), 7512 }, 7513 { 7514 op: "remove-snap-data-dir", 7515 name: "some-snap", 7516 path: filepath.Join(dirs.SnapDataDir, "some-snap"), 7517 }, 7518 { 7519 op: "remove-snap-files", 7520 path: filepath.Join(dirs.SnapMountDir, "some-snap/5"), 7521 stype: "app", 7522 }, 7523 { 7524 op: "discard-namespace", 7525 name: "some-snap", 7526 }, 7527 { 7528 op: "remove-snap-dir", 7529 name: "some-snap", 7530 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 7531 }, 7532 } 7533 // start with an easier-to-read error if this fails: 7534 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 7535 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 7536 7537 // verify snapSetup info 7538 tasks := ts.Tasks() 7539 revnos := []snap.Revision{{N: 7}, {N: 3}, {N: 5}} 7540 whichRevno := 0 7541 for _, t := range tasks { 7542 if t.Kind() == "run-hook" { 7543 continue 7544 } 7545 if t.Kind() == "save-snapshot" { 7546 continue 7547 } 7548 snapsup, err := snapstate.TaskSnapSetup(t) 7549 c.Assert(err, IsNil) 7550 7551 var expSnapSetup *snapstate.SnapSetup 7552 switch t.Kind() { 7553 case "discard-conns": 7554 expSnapSetup = &snapstate.SnapSetup{ 7555 SideInfo: &snap.SideInfo{ 7556 SnapID: "some-snap-id", 7557 RealName: "some-snap", 7558 }, 7559 } 7560 case "clear-snap", "discard-snap": 7561 expSnapSetup = &snapstate.SnapSetup{ 7562 SideInfo: &snap.SideInfo{ 7563 SnapID: "some-snap-id", 7564 RealName: "some-snap", 7565 Revision: revnos[whichRevno], 7566 }, 7567 } 7568 default: 7569 expSnapSetup = &snapstate.SnapSetup{ 7570 SideInfo: &snap.SideInfo{ 7571 SnapID: "some-snap-id", 7572 RealName: "some-snap", 7573 Revision: snap.R(7), 7574 }, 7575 Type: snap.TypeApp, 7576 PlugsOnly: true, 7577 } 7578 7579 } 7580 7581 c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind())) 7582 7583 if t.Kind() == "discard-snap" { 7584 whichRevno++ 7585 } 7586 } 7587 7588 // verify snaps in the system state 7589 var snapst snapstate.SnapState 7590 err = snapstate.Get(s.state, "some-snap", &snapst) 7591 c.Assert(err, Equals, state.ErrNoState) 7592 } 7593 7594 func (s *snapmgrTestSuite) TestRemoveOneRevisionRunThrough(c *C) { 7595 si3 := snap.SideInfo{ 7596 RealName: "some-snap", 7597 Revision: snap.R(3), 7598 } 7599 7600 si5 := snap.SideInfo{ 7601 RealName: "some-snap", 7602 Revision: snap.R(5), 7603 } 7604 7605 si7 := snap.SideInfo{ 7606 RealName: "some-snap", 7607 Revision: snap.R(7), 7608 } 7609 7610 s.state.Lock() 7611 defer s.state.Unlock() 7612 7613 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7614 Active: true, 7615 Sequence: []*snap.SideInfo{&si5, &si3, &si7}, 7616 Current: si7.Revision, 7617 SnapType: "app", 7618 }) 7619 7620 chg := s.state.NewChange("remove", "remove a snap") 7621 ts, err := snapstate.Remove(s.state, "some-snap", snap.R(3), nil) 7622 c.Assert(err, IsNil) 7623 chg.AddAll(ts) 7624 7625 s.state.Unlock() 7626 defer s.se.Stop() 7627 s.settle(c) 7628 s.state.Lock() 7629 7630 c.Check(len(s.fakeBackend.ops), Equals, 2) 7631 expected := fakeOps{ 7632 { 7633 op: "remove-snap-data", 7634 path: filepath.Join(dirs.SnapMountDir, "some-snap/3"), 7635 }, 7636 { 7637 op: "remove-snap-files", 7638 path: filepath.Join(dirs.SnapMountDir, "some-snap/3"), 7639 stype: "app", 7640 }, 7641 } 7642 // start with an easier-to-read error if this fails: 7643 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 7644 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 7645 7646 // verify snapSetup info 7647 tasks := ts.Tasks() 7648 for _, t := range tasks { 7649 if t.Kind() == "save-snapshot" { 7650 continue 7651 } 7652 snapsup, err := snapstate.TaskSnapSetup(t) 7653 c.Assert(err, IsNil) 7654 7655 expSnapSetup := &snapstate.SnapSetup{ 7656 SideInfo: &snap.SideInfo{ 7657 RealName: "some-snap", 7658 Revision: snap.R(3), 7659 }, 7660 } 7661 7662 c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind())) 7663 } 7664 7665 // verify snaps in the system state 7666 var snapst snapstate.SnapState 7667 err = snapstate.Get(s.state, "some-snap", &snapst) 7668 c.Assert(err, IsNil) 7669 c.Check(snapst.Sequence, HasLen, 2) 7670 } 7671 7672 func (s *snapmgrTestSuite) TestRemoveLastRevisionRunThrough(c *C) { 7673 si := snap.SideInfo{ 7674 RealName: "some-snap", 7675 Revision: snap.R(2), 7676 } 7677 7678 s.state.Lock() 7679 defer s.state.Unlock() 7680 7681 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7682 Active: false, 7683 Sequence: []*snap.SideInfo{&si}, 7684 Current: si.Revision, 7685 SnapType: "app", 7686 }) 7687 7688 chg := s.state.NewChange("remove", "remove a snap") 7689 ts, err := snapstate.Remove(s.state, "some-snap", snap.R(2), nil) 7690 c.Assert(err, IsNil) 7691 chg.AddAll(ts) 7692 7693 s.state.Unlock() 7694 defer s.se.Stop() 7695 s.settle(c) 7696 s.state.Lock() 7697 7698 c.Check(len(s.fakeBackend.ops), Equals, 7) 7699 expected := fakeOps{ 7700 { 7701 op: "auto-disconnect:Doing", 7702 name: "some-snap", 7703 revno: snap.R(2), 7704 }, 7705 { 7706 op: "remove-snap-data", 7707 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 7708 }, 7709 { 7710 op: "remove-snap-common-data", 7711 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 7712 }, 7713 { 7714 op: "remove-snap-data-dir", 7715 name: "some-snap", 7716 path: filepath.Join(dirs.SnapDataDir, "some-snap"), 7717 }, 7718 { 7719 op: "remove-snap-files", 7720 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 7721 stype: "app", 7722 }, 7723 { 7724 op: "discard-namespace", 7725 name: "some-snap", 7726 }, 7727 { 7728 op: "remove-snap-dir", 7729 name: "some-snap", 7730 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 7731 }, 7732 } 7733 // start with an easier-to-read error if this fails: 7734 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 7735 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 7736 7737 // verify snapSetup info 7738 tasks := ts.Tasks() 7739 for _, t := range tasks { 7740 if t.Kind() == "run-hook" { 7741 continue 7742 } 7743 if t.Kind() == "save-snapshot" { 7744 continue 7745 } 7746 snapsup, err := snapstate.TaskSnapSetup(t) 7747 c.Assert(err, IsNil) 7748 7749 expSnapSetup := &snapstate.SnapSetup{ 7750 SideInfo: &snap.SideInfo{ 7751 RealName: "some-snap", 7752 }, 7753 } 7754 if t.Kind() != "discard-conns" { 7755 expSnapSetup.SideInfo.Revision = snap.R(2) 7756 } 7757 if t.Kind() == "auto-disconnect" { 7758 expSnapSetup.PlugsOnly = true 7759 expSnapSetup.Type = "app" 7760 } 7761 7762 c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind())) 7763 } 7764 7765 // verify snaps in the system state 7766 var snapst snapstate.SnapState 7767 err = snapstate.Get(s.state, "some-snap", &snapst) 7768 c.Assert(err, Equals, state.ErrNoState) 7769 } 7770 7771 func (s *snapmgrTestSuite) TestRemoveCurrentActiveRevisionRefused(c *C) { 7772 si := snap.SideInfo{ 7773 RealName: "some-snap", 7774 Revision: snap.R(2), 7775 } 7776 7777 s.state.Lock() 7778 defer s.state.Unlock() 7779 7780 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7781 Active: true, 7782 Sequence: []*snap.SideInfo{&si}, 7783 Current: si.Revision, 7784 SnapType: "app", 7785 }) 7786 7787 _, err := snapstate.Remove(s.state, "some-snap", snap.R(2), nil) 7788 7789 c.Check(err, ErrorMatches, `cannot remove active revision 2 of snap "some-snap"`) 7790 } 7791 7792 func (s *snapmgrTestSuite) TestRemoveCurrentRevisionOfSeveralRefused(c *C) { 7793 si := snap.SideInfo{ 7794 RealName: "some-snap", 7795 Revision: snap.R(2), 7796 } 7797 7798 s.state.Lock() 7799 defer s.state.Unlock() 7800 7801 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7802 Active: true, 7803 Sequence: []*snap.SideInfo{&si, &si}, 7804 Current: si.Revision, 7805 SnapType: "app", 7806 }) 7807 7808 _, err := snapstate.Remove(s.state, "some-snap", snap.R(2), nil) 7809 c.Assert(err, NotNil) 7810 c.Check(err.Error(), Equals, `cannot remove active revision 2 of snap "some-snap" (revert first?)`) 7811 } 7812 7813 func (s *snapmgrTestSuite) TestRemoveMissingRevisionRefused(c *C) { 7814 si := snap.SideInfo{ 7815 RealName: "some-snap", 7816 Revision: snap.R(2), 7817 } 7818 7819 s.state.Lock() 7820 defer s.state.Unlock() 7821 7822 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7823 Active: true, 7824 Sequence: []*snap.SideInfo{&si}, 7825 Current: si.Revision, 7826 SnapType: "app", 7827 }) 7828 7829 _, err := snapstate.Remove(s.state, "some-snap", snap.R(1), nil) 7830 7831 c.Check(err, ErrorMatches, `revision 1 of snap "some-snap" is not installed`) 7832 } 7833 7834 func (s *snapmgrTestSuite) TestRemoveRefused(c *C) { 7835 si := snap.SideInfo{ 7836 RealName: "gadget", 7837 Revision: snap.R(7), 7838 } 7839 7840 s.state.Lock() 7841 defer s.state.Unlock() 7842 7843 snapstate.Set(s.state, "gadget", &snapstate.SnapState{ 7844 Active: true, 7845 Sequence: []*snap.SideInfo{&si}, 7846 Current: si.Revision, 7847 SnapType: "app", 7848 }) 7849 7850 _, err := snapstate.Remove(s.state, "gadget", snap.R(0), nil) 7851 7852 c.Check(err, ErrorMatches, `snap "gadget" is not removable`) 7853 } 7854 7855 func (s *snapmgrTestSuite) TestRemoveRefusedLastRevision(c *C) { 7856 si := snap.SideInfo{ 7857 RealName: "gadget", 7858 Revision: snap.R(7), 7859 } 7860 7861 s.state.Lock() 7862 defer s.state.Unlock() 7863 7864 snapstate.Set(s.state, "gadget", &snapstate.SnapState{ 7865 Active: false, 7866 Sequence: []*snap.SideInfo{&si}, 7867 Current: si.Revision, 7868 SnapType: "app", 7869 }) 7870 7871 _, err := snapstate.Remove(s.state, "gadget", snap.R(7), nil) 7872 7873 c.Check(err, ErrorMatches, `snap "gadget" is not removable`) 7874 } 7875 7876 func (s *snapmgrTestSuite) TestRemoveDeletesConfigOnLastRevision(c *C) { 7877 si := snap.SideInfo{ 7878 RealName: "some-snap", 7879 Revision: snap.R(7), 7880 } 7881 7882 s.state.Lock() 7883 defer s.state.Unlock() 7884 7885 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7886 Active: true, 7887 Sequence: []*snap.SideInfo{&si}, 7888 Current: si.Revision, 7889 SnapType: "app", 7890 }) 7891 7892 snapstate.Set(s.state, "another-snap", &snapstate.SnapState{ 7893 Active: true, 7894 Sequence: []*snap.SideInfo{&si}, 7895 Current: si.Revision, 7896 SnapType: "app", 7897 }) 7898 7899 tr := config.NewTransaction(s.state) 7900 tr.Set("some-snap", "foo", "bar") 7901 tr.Commit() 7902 7903 // a config for some other snap to verify its not accidentally destroyed 7904 tr = config.NewTransaction(s.state) 7905 tr.Set("another-snap", "bar", "baz") 7906 tr.Commit() 7907 7908 var res string 7909 tr = config.NewTransaction(s.state) 7910 c.Assert(tr.Get("some-snap", "foo", &res), IsNil) 7911 c.Assert(tr.Get("another-snap", "bar", &res), IsNil) 7912 7913 chg := s.state.NewChange("remove", "remove a snap") 7914 ts, err := snapstate.Remove(s.state, "some-snap", snap.R(0), nil) 7915 c.Assert(err, IsNil) 7916 chg.AddAll(ts) 7917 7918 s.state.Unlock() 7919 defer s.se.Stop() 7920 s.settle(c) 7921 s.state.Lock() 7922 7923 // verify snaps in the system state 7924 var snapst snapstate.SnapState 7925 err = snapstate.Get(s.state, "some-snap", &snapst) 7926 c.Assert(err, Equals, state.ErrNoState) 7927 7928 tr = config.NewTransaction(s.state) 7929 err = tr.Get("some-snap", "foo", &res) 7930 c.Assert(err, NotNil) 7931 c.Assert(err, ErrorMatches, `snap "some-snap" has no "foo" configuration option`) 7932 7933 // and another snap has its config intact 7934 c.Assert(tr.Get("another-snap", "bar", &res), IsNil) 7935 c.Assert(res, Equals, "baz") 7936 } 7937 7938 func (s *snapmgrTestSuite) TestRemoveDoesntDeleteConfigIfNotLastRevision(c *C) { 7939 si1 := snap.SideInfo{ 7940 RealName: "some-snap", 7941 Revision: snap.R(7), 7942 } 7943 si2 := snap.SideInfo{ 7944 RealName: "some-snap", 7945 Revision: snap.R(8), 7946 } 7947 7948 s.state.Lock() 7949 defer s.state.Unlock() 7950 7951 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7952 Active: true, 7953 Sequence: []*snap.SideInfo{&si1, &si2}, 7954 Current: si2.Revision, 7955 SnapType: "app", 7956 }) 7957 7958 tr := config.NewTransaction(s.state) 7959 tr.Set("some-snap", "foo", "bar") 7960 tr.Commit() 7961 7962 var res string 7963 tr = config.NewTransaction(s.state) 7964 c.Assert(tr.Get("some-snap", "foo", &res), IsNil) 7965 7966 chg := s.state.NewChange("remove", "remove a snap") 7967 ts, err := snapstate.Remove(s.state, "some-snap", si1.Revision, nil) 7968 c.Assert(err, IsNil) 7969 chg.AddAll(ts) 7970 7971 s.state.Unlock() 7972 defer s.se.Stop() 7973 s.settle(c) 7974 s.state.Lock() 7975 7976 // verify snaps in the system state 7977 var snapst snapstate.SnapState 7978 err = snapstate.Get(s.state, "some-snap", &snapst) 7979 c.Assert(err, IsNil) 7980 7981 tr = config.NewTransaction(s.state) 7982 c.Assert(tr.Get("some-snap", "foo", &res), IsNil) 7983 c.Assert(res, Equals, "bar") 7984 } 7985 7986 func (s *snapmgrTestSuite) TestUpdateMakesConfigSnapshot(c *C) { 7987 s.state.Lock() 7988 defer s.state.Unlock() 7989 7990 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 7991 Active: true, 7992 Sequence: []*snap.SideInfo{ 7993 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 7994 }, 7995 Current: snap.R(1), 7996 SnapType: "app", 7997 }) 7998 7999 tr := config.NewTransaction(s.state) 8000 tr.Set("some-snap", "foo", "bar") 8001 tr.Commit() 8002 8003 var cfgs map[string]interface{} 8004 // we don't have config snapshots yet 8005 c.Assert(s.state.Get("revision-config", &cfgs), Equals, state.ErrNoState) 8006 8007 chg := s.state.NewChange("update", "update a snap") 8008 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)} 8009 ts, err := snapstate.Update(s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 8010 c.Assert(err, IsNil) 8011 chg.AddAll(ts) 8012 8013 s.state.Unlock() 8014 defer s.se.Stop() 8015 s.settle(c) 8016 8017 s.state.Lock() 8018 cfgs = nil 8019 // config copy of rev. 1 has been made 8020 c.Assert(s.state.Get("revision-config", &cfgs), IsNil) 8021 c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{ 8022 "1": map[string]interface{}{ 8023 "foo": "bar", 8024 }, 8025 }) 8026 } 8027 8028 func (s *snapmgrTestSuite) TestRevertRestoresConfigSnapshot(c *C) { 8029 s.state.Lock() 8030 defer s.state.Unlock() 8031 8032 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8033 Active: true, 8034 Sequence: []*snap.SideInfo{ 8035 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 8036 {RealName: "some-snap", Revision: snap.R(2)}, 8037 }, 8038 Current: snap.R(2), 8039 SnapType: "app", 8040 }) 8041 8042 // set configuration for current snap 8043 tr := config.NewTransaction(s.state) 8044 tr.Set("some-snap", "foo", "100") 8045 tr.Commit() 8046 8047 // make config snapshot for rev.1 8048 config.SaveRevisionConfig(s.state, "some-snap", snap.R(1)) 8049 8050 // modify for rev. 2 8051 tr = config.NewTransaction(s.state) 8052 tr.Set("some-snap", "foo", "200") 8053 tr.Commit() 8054 8055 chg := s.state.NewChange("revert", "revert snap") 8056 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 8057 c.Assert(err, IsNil) 8058 chg.AddAll(ts) 8059 8060 s.state.Unlock() 8061 defer s.se.Stop() 8062 s.settle(c) 8063 8064 s.state.Lock() 8065 // config snapshot of rev. 2 has been made by 'revert' 8066 var cfgs map[string]interface{} 8067 c.Assert(s.state.Get("revision-config", &cfgs), IsNil) 8068 c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{ 8069 "1": map[string]interface{}{"foo": "100"}, 8070 "2": map[string]interface{}{"foo": "200"}, 8071 }) 8072 8073 // current snap configuration has been restored from rev. 1 config snapshot 8074 tr = config.NewTransaction(s.state) 8075 var res string 8076 c.Assert(tr.Get("some-snap", "foo", &res), IsNil) 8077 c.Assert(res, Equals, "100") 8078 } 8079 8080 func (s *snapmgrTestSuite) TestRefreshDoesntRestoreRevisionConfig(c *C) { 8081 restore := release.MockOnClassic(false) 8082 defer restore() 8083 8084 s.state.Lock() 8085 defer s.state.Unlock() 8086 8087 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8088 Active: true, 8089 Sequence: []*snap.SideInfo{ 8090 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 8091 }, 8092 Current: snap.R(1), 8093 SnapType: "app", 8094 }) 8095 8096 // set global configuration (affecting current snap) 8097 tr := config.NewTransaction(s.state) 8098 tr.Set("some-snap", "foo", "100") 8099 tr.Commit() 8100 8101 // set per-revision config for the upcoming rev. 2, we don't expect it restored though 8102 // since only revert restores revision configs. 8103 s.state.Set("revision-config", map[string]interface{}{ 8104 "some-snap": map[string]interface{}{ 8105 "2": map[string]interface{}{"foo": "200"}, 8106 }, 8107 }) 8108 8109 // simulate a refresh to rev. 2 8110 chg := s.state.NewChange("update", "update some-snap") 8111 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)}, s.user.ID, snapstate.Flags{}) 8112 c.Assert(err, IsNil) 8113 chg.AddAll(ts) 8114 8115 s.state.Unlock() 8116 defer s.se.Stop() 8117 s.settle(c) 8118 8119 s.state.Lock() 8120 // config of rev. 1 has been stored in per-revision map 8121 var cfgs map[string]interface{} 8122 c.Assert(s.state.Get("revision-config", &cfgs), IsNil) 8123 c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{ 8124 "1": map[string]interface{}{"foo": "100"}, 8125 "2": map[string]interface{}{"foo": "200"}, 8126 }) 8127 8128 // config of rev. 2 hasn't been restored by refresh, old value returned 8129 tr = config.NewTransaction(s.state) 8130 var res string 8131 c.Assert(tr.Get("some-snap", "foo", &res), IsNil) 8132 c.Assert(res, Equals, "100") 8133 } 8134 8135 func (s *snapmgrTestSuite) TestUpdateDoesGC(c *C) { 8136 s.state.Lock() 8137 defer s.state.Unlock() 8138 restore := release.MockOnClassic(false) 8139 defer restore() 8140 8141 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8142 Active: true, 8143 Sequence: []*snap.SideInfo{ 8144 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 8145 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 8146 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 8147 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}, 8148 }, 8149 Current: snap.R(4), 8150 SnapType: "app", 8151 }) 8152 8153 chg := s.state.NewChange("update", "update a snap") 8154 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 8155 c.Assert(err, IsNil) 8156 chg.AddAll(ts) 8157 8158 s.state.Unlock() 8159 defer s.se.Stop() 8160 s.settle(c) 8161 s.state.Lock() 8162 8163 // ensure garbage collection runs as the last tasks 8164 expectedTail := fakeOps{ 8165 { 8166 op: "link-snap", 8167 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 8168 }, 8169 { 8170 op: "auto-connect:Doing", 8171 name: "some-snap", 8172 revno: snap.R(11), 8173 }, 8174 { 8175 op: "update-aliases", 8176 }, 8177 { 8178 op: "remove-snap-data", 8179 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 8180 }, 8181 { 8182 op: "remove-snap-files", 8183 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 8184 stype: "app", 8185 }, 8186 { 8187 op: "remove-snap-data", 8188 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 8189 }, 8190 { 8191 op: "remove-snap-files", 8192 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 8193 stype: "app", 8194 }, 8195 { 8196 op: "cleanup-trash", 8197 name: "some-snap", 8198 revno: snap.R(11), 8199 }, 8200 } 8201 8202 opsTail := s.fakeBackend.ops[len(s.fakeBackend.ops)-len(expectedTail):] 8203 c.Assert(opsTail.Ops(), DeepEquals, expectedTail.Ops()) 8204 c.Check(opsTail, DeepEquals, expectedTail) 8205 } 8206 8207 func (s *snapmgrTestSuite) TestRevertNoRevertAgain(c *C) { 8208 siNew := snap.SideInfo{ 8209 RealName: "some-snap", 8210 Revision: snap.R(77), 8211 } 8212 8213 si := snap.SideInfo{ 8214 RealName: "some-snap", 8215 Revision: snap.R(7), 8216 } 8217 8218 s.state.Lock() 8219 defer s.state.Unlock() 8220 8221 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8222 Active: true, 8223 Sequence: []*snap.SideInfo{&si, &siNew}, 8224 Current: snap.R(7), 8225 }) 8226 8227 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 8228 c.Assert(err, ErrorMatches, "no revision to revert to") 8229 c.Assert(ts, IsNil) 8230 } 8231 8232 func (s *snapmgrTestSuite) TestRevertNothingToRevertTo(c *C) { 8233 si := snap.SideInfo{ 8234 RealName: "some-snap", 8235 Revision: snap.R(7), 8236 } 8237 8238 s.state.Lock() 8239 defer s.state.Unlock() 8240 8241 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8242 Active: true, 8243 Sequence: []*snap.SideInfo{&si}, 8244 Current: si.Revision, 8245 }) 8246 8247 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 8248 c.Assert(err, ErrorMatches, "no revision to revert to") 8249 c.Assert(ts, IsNil) 8250 } 8251 8252 func (s *snapmgrTestSuite) TestRevertToRevisionNoValidVersion(c *C) { 8253 si := snap.SideInfo{ 8254 RealName: "some-snap", 8255 Revision: snap.R(7), 8256 } 8257 si2 := snap.SideInfo{ 8258 RealName: "some-snap", 8259 Revision: snap.R(77), 8260 } 8261 8262 s.state.Lock() 8263 defer s.state.Unlock() 8264 8265 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8266 Active: true, 8267 Sequence: []*snap.SideInfo{&si, &si2}, 8268 Current: snap.R(77), 8269 }) 8270 8271 ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("99"), snapstate.Flags{}) 8272 c.Assert(err, ErrorMatches, `cannot find revision 99 for snap "some-snap"`) 8273 c.Assert(ts, IsNil) 8274 } 8275 8276 func (s *snapmgrTestSuite) TestRevertToRevisionAlreadyCurrent(c *C) { 8277 si := snap.SideInfo{ 8278 RealName: "some-snap", 8279 Revision: snap.R(7), 8280 } 8281 si2 := snap.SideInfo{ 8282 RealName: "some-snap", 8283 Revision: snap.R(77), 8284 } 8285 8286 s.state.Lock() 8287 defer s.state.Unlock() 8288 8289 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8290 Active: true, 8291 Sequence: []*snap.SideInfo{&si, &si2}, 8292 Current: snap.R(77), 8293 }) 8294 8295 ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("77"), snapstate.Flags{}) 8296 c.Assert(err, ErrorMatches, `already on requested revision`) 8297 c.Assert(ts, IsNil) 8298 } 8299 8300 func (s *snapmgrTestSuite) TestRevertRunThrough(c *C) { 8301 si := snap.SideInfo{ 8302 RealName: "some-snap", 8303 Revision: snap.R(7), 8304 } 8305 siOld := snap.SideInfo{ 8306 RealName: "some-snap", 8307 Revision: snap.R(2), 8308 } 8309 8310 s.state.Lock() 8311 defer s.state.Unlock() 8312 8313 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8314 Active: true, 8315 SnapType: "app", 8316 Sequence: []*snap.SideInfo{&siOld, &si}, 8317 Current: si.Revision, 8318 }) 8319 8320 chg := s.state.NewChange("revert", "revert a snap backwards") 8321 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 8322 c.Assert(err, IsNil) 8323 chg.AddAll(ts) 8324 8325 s.state.Unlock() 8326 defer s.se.Stop() 8327 s.settle(c) 8328 s.state.Lock() 8329 8330 expected := fakeOps{ 8331 { 8332 op: "remove-snap-aliases", 8333 name: "some-snap", 8334 }, 8335 { 8336 op: "unlink-snap", 8337 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 8338 }, 8339 { 8340 op: "setup-profiles:Doing", 8341 name: "some-snap", 8342 revno: snap.R(2), 8343 }, 8344 { 8345 op: "candidate", 8346 sinfo: snap.SideInfo{ 8347 RealName: "some-snap", 8348 Revision: snap.R(2), 8349 }, 8350 }, 8351 { 8352 op: "link-snap", 8353 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 8354 }, 8355 { 8356 op: "auto-connect:Doing", 8357 name: "some-snap", 8358 revno: snap.R(2), 8359 }, 8360 { 8361 op: "update-aliases", 8362 }, 8363 } 8364 // start with an easier-to-read error if this fails: 8365 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 8366 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 8367 8368 // verify that the R(2) version is active now and R(7) is still there 8369 var snapst snapstate.SnapState 8370 err = snapstate.Get(s.state, "some-snap", &snapst) 8371 c.Assert(err, IsNil) 8372 8373 c.Assert(snapst.Active, Equals, true) 8374 c.Assert(snapst.Current, Equals, snap.R(2)) 8375 c.Assert(snapst.Sequence, HasLen, 2) 8376 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 8377 RealName: "some-snap", 8378 Channel: "", 8379 Revision: snap.R(2), 8380 }) 8381 c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{ 8382 RealName: "some-snap", 8383 Channel: "", 8384 Revision: snap.R(7), 8385 }) 8386 c.Assert(snapst.Block(), DeepEquals, []snap.Revision{snap.R(7)}) 8387 } 8388 8389 func (s *snapmgrTestSuite) TestParallelInstanceRevertRunThrough(c *C) { 8390 si := snap.SideInfo{ 8391 RealName: "some-snap", 8392 Revision: snap.R(7), 8393 } 8394 siOld := snap.SideInfo{ 8395 RealName: "some-snap", 8396 Revision: snap.R(2), 8397 } 8398 8399 s.state.Lock() 8400 defer s.state.Unlock() 8401 8402 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 8403 Active: true, 8404 SnapType: "app", 8405 Sequence: []*snap.SideInfo{&siOld, &si}, 8406 Current: si.Revision, 8407 InstanceKey: "instance", 8408 }) 8409 8410 // another snap withouth instance key 8411 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8412 Active: true, 8413 SnapType: "app", 8414 Sequence: []*snap.SideInfo{&siOld, &si}, 8415 Current: si.Revision, 8416 }) 8417 8418 chg := s.state.NewChange("revert", "revert a snap backwards") 8419 ts, err := snapstate.Revert(s.state, "some-snap_instance", snapstate.Flags{}) 8420 c.Assert(err, IsNil) 8421 chg.AddAll(ts) 8422 8423 s.state.Unlock() 8424 defer s.se.Stop() 8425 s.settle(c) 8426 s.state.Lock() 8427 8428 expected := fakeOps{ 8429 { 8430 op: "remove-snap-aliases", 8431 name: "some-snap_instance", 8432 }, 8433 { 8434 op: "unlink-snap", 8435 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 8436 }, 8437 { 8438 op: "setup-profiles:Doing", 8439 name: "some-snap_instance", 8440 revno: snap.R(2), 8441 }, 8442 { 8443 op: "candidate", 8444 sinfo: snap.SideInfo{ 8445 RealName: "some-snap", 8446 Revision: snap.R(2), 8447 }, 8448 }, 8449 { 8450 op: "link-snap", 8451 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/2"), 8452 }, 8453 { 8454 op: "auto-connect:Doing", 8455 name: "some-snap_instance", 8456 revno: snap.R(2), 8457 }, 8458 { 8459 op: "update-aliases", 8460 }, 8461 } 8462 // start with an easier-to-read error if this fails: 8463 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 8464 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 8465 8466 // verify that the R(2) version is active now and R(7) is still there 8467 var snapst snapstate.SnapState 8468 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 8469 c.Assert(err, IsNil) 8470 8471 c.Assert(snapst.Active, Equals, true) 8472 c.Assert(snapst.Current, Equals, snap.R(2)) 8473 c.Assert(snapst.InstanceKey, Equals, "instance") 8474 c.Assert(snapst.Sequence, HasLen, 2) 8475 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 8476 RealName: "some-snap", 8477 Channel: "", 8478 Revision: snap.R(2), 8479 }) 8480 c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{ 8481 RealName: "some-snap", 8482 Channel: "", 8483 Revision: snap.R(7), 8484 }) 8485 c.Assert(snapst.Block(), DeepEquals, []snap.Revision{snap.R(7)}) 8486 8487 // non instance snap is not affected 8488 var nonInstanceSnapst snapstate.SnapState 8489 err = snapstate.Get(s.state, "some-snap", &nonInstanceSnapst) 8490 c.Assert(err, IsNil) 8491 c.Assert(nonInstanceSnapst.Current, Equals, snap.R(7)) 8492 8493 } 8494 8495 func (s *snapmgrTestSuite) TestRevertWithLocalRevisionRunThrough(c *C) { 8496 si := snap.SideInfo{ 8497 RealName: "some-snap", 8498 Revision: snap.R(-7), 8499 } 8500 siOld := snap.SideInfo{ 8501 RealName: "some-snap", 8502 Revision: snap.R(-2), 8503 } 8504 8505 s.state.Lock() 8506 defer s.state.Unlock() 8507 8508 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8509 Active: true, 8510 SnapType: "app", 8511 Sequence: []*snap.SideInfo{&siOld, &si}, 8512 Current: si.Revision, 8513 }) 8514 8515 chg := s.state.NewChange("revert", "revert a snap backwards") 8516 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 8517 c.Assert(err, IsNil) 8518 chg.AddAll(ts) 8519 8520 s.state.Unlock() 8521 defer s.se.Stop() 8522 s.settle(c) 8523 s.state.Lock() 8524 8525 c.Assert(s.fakeBackend.ops.Ops(), HasLen, 7) 8526 8527 // verify that LocalRevision is still -7 8528 var snapst snapstate.SnapState 8529 err = snapstate.Get(s.state, "some-snap", &snapst) 8530 c.Assert(err, IsNil) 8531 8532 c.Assert(snapst.LocalRevision(), Equals, snap.R(-7)) 8533 } 8534 8535 func (s *snapmgrTestSuite) TestRevertToRevisionNewVersion(c *C) { 8536 siNew := snap.SideInfo{ 8537 RealName: "some-snap", 8538 Revision: snap.R(7), 8539 SnapID: "october", 8540 } 8541 8542 si := snap.SideInfo{ 8543 RealName: "some-snap", 8544 Revision: snap.R(2), 8545 SnapID: "october", 8546 } 8547 8548 s.state.Lock() 8549 defer s.state.Unlock() 8550 8551 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8552 Active: true, 8553 SnapType: "app", 8554 Sequence: []*snap.SideInfo{&si, &siNew}, 8555 Current: snap.R(2), 8556 Channel: "edge", 8557 }) 8558 8559 chg := s.state.NewChange("revert", "revert a snap forward") 8560 ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(7), snapstate.Flags{}) 8561 c.Assert(err, IsNil) 8562 chg.AddAll(ts) 8563 8564 s.state.Unlock() 8565 defer s.se.Stop() 8566 s.settle(c) 8567 s.state.Lock() 8568 8569 expected := fakeOps{ 8570 { 8571 op: "remove-snap-aliases", 8572 name: "some-snap", 8573 }, 8574 { 8575 op: "unlink-snap", 8576 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 8577 }, 8578 { 8579 op: "setup-profiles:Doing", 8580 name: "some-snap", 8581 revno: snap.R(7), 8582 }, 8583 { 8584 op: "candidate", 8585 sinfo: siNew, 8586 }, 8587 { 8588 op: "link-snap", 8589 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 8590 }, 8591 { 8592 op: "auto-connect:Doing", 8593 name: "some-snap", 8594 revno: snap.R(7), 8595 }, 8596 { 8597 op: "update-aliases", 8598 }, 8599 } 8600 // start with an easier-to-read error if this fails: 8601 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 8602 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 8603 8604 // verify that the R(7) version is active now 8605 var snapst snapstate.SnapState 8606 err = snapstate.Get(s.state, "some-snap", &snapst) 8607 c.Assert(err, IsNil) 8608 8609 c.Check(snapst.Active, Equals, true) 8610 c.Check(snapst.Current, Equals, snap.R(7)) 8611 c.Check(snapst.Sequence, HasLen, 2) 8612 c.Check(snapst.Channel, Equals, "edge") 8613 c.Check(snapst.CurrentSideInfo(), DeepEquals, &siNew) 8614 8615 c.Check(snapst.Block(), HasLen, 0) 8616 } 8617 8618 func (s *snapmgrTestSuite) TestRevertTotalUndoRunThrough(c *C) { 8619 si := snap.SideInfo{ 8620 RealName: "some-snap", 8621 Revision: snap.R(1), 8622 } 8623 si2 := snap.SideInfo{ 8624 RealName: "some-snap", 8625 Revision: snap.R(2), 8626 } 8627 8628 s.state.Lock() 8629 defer s.state.Unlock() 8630 8631 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8632 Active: true, 8633 SnapType: "app", 8634 Sequence: []*snap.SideInfo{&si, &si2}, 8635 Current: si2.Revision, 8636 }) 8637 8638 chg := s.state.NewChange("revert", "revert a snap") 8639 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 8640 c.Assert(err, IsNil) 8641 chg.AddAll(ts) 8642 8643 tasks := ts.Tasks() 8644 last := tasks[len(tasks)-1] 8645 8646 terr := s.state.NewTask("error-trigger", "provoking total undo") 8647 terr.WaitFor(last) 8648 chg.AddTask(terr) 8649 8650 s.state.Unlock() 8651 defer s.se.Stop() 8652 s.settle(c) 8653 s.state.Lock() 8654 8655 expected := fakeOps{ 8656 { 8657 op: "remove-snap-aliases", 8658 name: "some-snap", 8659 }, 8660 { 8661 op: "unlink-snap", 8662 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 8663 }, 8664 { 8665 op: "setup-profiles:Doing", 8666 name: "some-snap", 8667 revno: snap.R(1), 8668 }, 8669 { 8670 op: "candidate", 8671 sinfo: snap.SideInfo{ 8672 RealName: "some-snap", 8673 Revision: snap.R(1), 8674 }, 8675 }, 8676 { 8677 op: "link-snap", 8678 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 8679 }, 8680 { 8681 op: "auto-connect:Doing", 8682 name: "some-snap", 8683 revno: snap.R(1), 8684 }, 8685 { 8686 op: "update-aliases", 8687 }, 8688 // undoing everything from here down... 8689 { 8690 op: "remove-snap-aliases", 8691 name: "some-snap", 8692 }, 8693 { 8694 op: "unlink-snap", 8695 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 8696 }, 8697 { 8698 op: "setup-profiles:Undoing", 8699 name: "some-snap", 8700 revno: snap.R(1), 8701 }, 8702 { 8703 op: "link-snap", 8704 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 8705 }, 8706 { 8707 op: "update-aliases", 8708 }, 8709 } 8710 // start with an easier-to-read error if this fails: 8711 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 8712 c.Check(s.fakeBackend.ops, DeepEquals, expected) 8713 8714 // verify snaps in the system state 8715 var snapst snapstate.SnapState 8716 err = snapstate.Get(s.state, "some-snap", &snapst) 8717 c.Assert(err, IsNil) 8718 8719 c.Assert(snapst.Active, Equals, true) 8720 c.Assert(snapst.Sequence, HasLen, 2) 8721 c.Assert(snapst.Current, Equals, si2.Revision) 8722 } 8723 8724 func (s *snapmgrTestSuite) TestRevertUndoRunThrough(c *C) { 8725 si := snap.SideInfo{ 8726 RealName: "some-snap", 8727 Revision: snap.R(1), 8728 } 8729 si2 := snap.SideInfo{ 8730 RealName: "some-snap", 8731 Revision: snap.R(2), 8732 } 8733 8734 s.state.Lock() 8735 defer s.state.Unlock() 8736 8737 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8738 Active: true, 8739 SnapType: "app", 8740 Sequence: []*snap.SideInfo{&si, &si2}, 8741 Current: si2.Revision, 8742 }) 8743 8744 chg := s.state.NewChange("revert", "install a revert") 8745 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 8746 c.Assert(err, IsNil) 8747 chg.AddAll(ts) 8748 8749 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/1") 8750 8751 s.state.Unlock() 8752 defer s.se.Stop() 8753 s.settle(c) 8754 s.state.Lock() 8755 8756 expected := fakeOps{ 8757 { 8758 op: "remove-snap-aliases", 8759 name: "some-snap", 8760 }, 8761 { 8762 op: "unlink-snap", 8763 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 8764 }, 8765 { 8766 op: "setup-profiles:Doing", 8767 name: "some-snap", 8768 revno: snap.R(1), 8769 }, 8770 { 8771 op: "candidate", 8772 sinfo: snap.SideInfo{ 8773 RealName: "some-snap", 8774 Revision: snap.R(1), 8775 }, 8776 }, 8777 { 8778 op: "link-snap.failed", 8779 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 8780 }, 8781 // undo stuff here 8782 { 8783 op: "unlink-snap", 8784 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 8785 }, 8786 { 8787 op: "setup-profiles:Undoing", 8788 name: "some-snap", 8789 revno: snap.R(1), 8790 }, 8791 { 8792 op: "link-snap", 8793 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 8794 }, 8795 { 8796 op: "update-aliases", 8797 }, 8798 } 8799 8800 // ensure all our tasks ran 8801 // start with an easier-to-read error if this fails: 8802 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 8803 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 8804 8805 // verify snaps in the system state 8806 var snapst snapstate.SnapState 8807 err = snapstate.Get(s.state, "some-snap", &snapst) 8808 c.Assert(err, IsNil) 8809 8810 c.Assert(snapst.Active, Equals, true) 8811 c.Assert(snapst.Sequence, HasLen, 2) 8812 c.Assert(snapst.Current, Equals, snap.R(2)) 8813 } 8814 8815 func (s *snapmgrTestSuite) TestEnableDoesNotEnableAgain(c *C) { 8816 si := snap.SideInfo{ 8817 RealName: "some-snap", 8818 Revision: snap.R(7), 8819 } 8820 8821 s.state.Lock() 8822 defer s.state.Unlock() 8823 8824 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8825 Sequence: []*snap.SideInfo{&si}, 8826 Current: snap.R(7), 8827 Active: true, 8828 }) 8829 8830 ts, err := snapstate.Enable(s.state, "some-snap") 8831 c.Assert(err, ErrorMatches, `snap "some-snap" already enabled`) 8832 c.Assert(ts, IsNil) 8833 } 8834 8835 func (s *snapmgrTestSuite) TestEnableRunThrough(c *C) { 8836 si := snap.SideInfo{ 8837 RealName: "some-snap", 8838 Revision: snap.R(7), 8839 Channel: "edge", 8840 SnapID: "foo", 8841 } 8842 8843 s.state.Lock() 8844 defer s.state.Unlock() 8845 8846 flags := snapstate.Flags{ 8847 DevMode: true, 8848 JailMode: true, 8849 Classic: true, 8850 TryMode: true, 8851 Required: true, 8852 } 8853 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8854 Sequence: []*snap.SideInfo{&si}, 8855 Current: si.Revision, 8856 Active: false, 8857 Channel: "edge", 8858 Flags: flags, 8859 AliasesPending: true, 8860 AutoAliasesDisabled: true, 8861 }) 8862 8863 chg := s.state.NewChange("enable", "enable a snap") 8864 ts, err := snapstate.Enable(s.state, "some-snap") 8865 c.Assert(err, IsNil) 8866 chg.AddAll(ts) 8867 8868 s.state.Unlock() 8869 defer s.se.Stop() 8870 s.settle(c) 8871 s.state.Lock() 8872 8873 expected := fakeOps{ 8874 { 8875 op: "setup-profiles:Doing", 8876 name: "some-snap", 8877 revno: snap.R(7), 8878 }, 8879 { 8880 op: "candidate", 8881 sinfo: si, 8882 }, 8883 { 8884 op: "link-snap", 8885 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 8886 }, 8887 { 8888 op: "auto-connect:Doing", 8889 name: "some-snap", 8890 revno: snap.R(7), 8891 }, 8892 { 8893 op: "update-aliases", 8894 }, 8895 } 8896 // start with an easier-to-read error if this fails: 8897 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 8898 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 8899 8900 var snapst snapstate.SnapState 8901 err = snapstate.Get(s.state, "some-snap", &snapst) 8902 c.Assert(err, IsNil) 8903 c.Check(snapst.Flags, DeepEquals, flags) 8904 8905 c.Assert(snapst.Active, Equals, true) 8906 c.Assert(snapst.AliasesPending, Equals, false) 8907 c.Assert(snapst.AutoAliasesDisabled, Equals, true) 8908 8909 info, err := snapst.CurrentInfo() 8910 c.Assert(err, IsNil) 8911 c.Assert(info.Channel, Equals, "edge") 8912 c.Assert(info.SnapID, Equals, "foo") 8913 8914 first := ts.Tasks()[0] 8915 snapsup, err := snapstate.TaskSnapSetup(first) 8916 c.Assert(err, IsNil) 8917 c.Check(snapsup, DeepEquals, &snapstate.SnapSetup{ 8918 SideInfo: &si, 8919 Flags: flags, 8920 Type: snap.TypeApp, 8921 PlugsOnly: true, 8922 }) 8923 } 8924 8925 func (s *snapmgrTestSuite) TestDisableRunThrough(c *C) { 8926 si := snap.SideInfo{ 8927 RealName: "some-snap", 8928 Revision: snap.R(7), 8929 } 8930 8931 s.state.Lock() 8932 defer s.state.Unlock() 8933 8934 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 8935 Sequence: []*snap.SideInfo{&si}, 8936 Current: si.Revision, 8937 Active: true, 8938 SnapType: "app", 8939 }) 8940 8941 chg := s.state.NewChange("disable", "disable a snap") 8942 ts, err := snapstate.Disable(s.state, "some-snap") 8943 c.Assert(err, IsNil) 8944 chg.AddAll(ts) 8945 8946 s.state.Unlock() 8947 defer s.se.Stop() 8948 s.settle(c) 8949 s.state.Lock() 8950 8951 expected := fakeOps{ 8952 { 8953 op: "remove-snap-aliases", 8954 name: "some-snap", 8955 }, 8956 { 8957 op: "unlink-snap", 8958 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 8959 }, 8960 { 8961 op: "remove-profiles:Doing", 8962 name: "some-snap", 8963 revno: snap.R(7), 8964 }, 8965 } 8966 // start with an easier-to-read error if this fails: 8967 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 8968 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 8969 8970 var snapst snapstate.SnapState 8971 err = snapstate.Get(s.state, "some-snap", &snapst) 8972 c.Assert(err, IsNil) 8973 8974 c.Assert(snapst.Active, Equals, false) 8975 c.Assert(snapst.AliasesPending, Equals, true) 8976 8977 first := ts.Tasks()[0] 8978 snapsup, err := snapstate.TaskSnapSetup(first) 8979 c.Assert(err, IsNil) 8980 c.Check(snapsup, DeepEquals, &snapstate.SnapSetup{ 8981 SideInfo: &snap.SideInfo{ 8982 RealName: "some-snap", 8983 Revision: snap.R(7), 8984 }, 8985 Type: snap.TypeApp, 8986 PlugsOnly: true, 8987 }) 8988 } 8989 8990 func (s *snapmgrTestSuite) TestParallelInstanceEnableRunThrough(c *C) { 8991 si := snap.SideInfo{ 8992 RealName: "some-snap", 8993 Revision: snap.R(7), 8994 Channel: "edge", 8995 SnapID: "foo", 8996 } 8997 8998 s.state.Lock() 8999 defer s.state.Unlock() 9000 9001 flags := snapstate.Flags{ 9002 DevMode: true, 9003 JailMode: true, 9004 Classic: true, 9005 TryMode: true, 9006 Required: true, 9007 } 9008 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 9009 Sequence: []*snap.SideInfo{&si}, 9010 Current: si.Revision, 9011 Active: false, 9012 Channel: "edge", 9013 Flags: flags, 9014 AliasesPending: true, 9015 AutoAliasesDisabled: true, 9016 InstanceKey: "instance", 9017 }) 9018 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 9019 Sequence: []*snap.SideInfo{&si}, 9020 Current: si.Revision, 9021 Active: false, 9022 Channel: "edge", 9023 Flags: flags, 9024 AliasesPending: true, 9025 AutoAliasesDisabled: true, 9026 }) 9027 9028 chg := s.state.NewChange("enable", "enable a snap") 9029 ts, err := snapstate.Enable(s.state, "some-snap_instance") 9030 c.Assert(err, IsNil) 9031 chg.AddAll(ts) 9032 9033 s.state.Unlock() 9034 s.settle(c) 9035 s.state.Lock() 9036 9037 expected := fakeOps{ 9038 { 9039 op: "setup-profiles:Doing", 9040 name: "some-snap_instance", 9041 revno: snap.R(7), 9042 }, 9043 { 9044 op: "candidate", 9045 sinfo: si, 9046 }, 9047 { 9048 op: "link-snap", 9049 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 9050 }, 9051 { 9052 op: "auto-connect:Doing", 9053 name: "some-snap_instance", 9054 revno: snap.R(7), 9055 }, 9056 { 9057 op: "update-aliases", 9058 }, 9059 } 9060 // start with an easier-to-read error if this fails: 9061 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 9062 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 9063 9064 var snapst snapstate.SnapState 9065 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 9066 c.Assert(err, IsNil) 9067 c.Check(snapst.Flags, DeepEquals, flags) 9068 9069 c.Assert(snapst.InstanceKey, Equals, "instance") 9070 c.Assert(snapst.Active, Equals, true) 9071 c.Assert(snapst.AliasesPending, Equals, false) 9072 c.Assert(snapst.AutoAliasesDisabled, Equals, true) 9073 9074 info, err := snapst.CurrentInfo() 9075 c.Assert(err, IsNil) 9076 c.Assert(info.Channel, Equals, "edge") 9077 c.Assert(info.SnapID, Equals, "foo") 9078 9079 // the non-parallel instance is still disabled 9080 snapst = snapstate.SnapState{} 9081 err = snapstate.Get(s.state, "some-snap", &snapst) 9082 c.Assert(err, IsNil) 9083 c.Assert(snapst.InstanceKey, Equals, "") 9084 c.Assert(snapst.Active, Equals, false) 9085 } 9086 9087 func (s *snapmgrTestSuite) TestParallelInstanceDisableRunThrough(c *C) { 9088 si := snap.SideInfo{ 9089 RealName: "some-snap", 9090 Revision: snap.R(7), 9091 } 9092 9093 s.state.Lock() 9094 defer s.state.Unlock() 9095 9096 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 9097 Sequence: []*snap.SideInfo{&si}, 9098 Current: si.Revision, 9099 Active: true, 9100 }) 9101 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 9102 Sequence: []*snap.SideInfo{&si}, 9103 Current: si.Revision, 9104 Active: true, 9105 InstanceKey: "instance", 9106 }) 9107 9108 chg := s.state.NewChange("disable", "disable a snap") 9109 ts, err := snapstate.Disable(s.state, "some-snap_instance") 9110 c.Assert(err, IsNil) 9111 chg.AddAll(ts) 9112 9113 s.state.Unlock() 9114 s.settle(c) 9115 s.state.Lock() 9116 9117 expected := fakeOps{ 9118 { 9119 op: "remove-snap-aliases", 9120 name: "some-snap_instance", 9121 }, 9122 { 9123 op: "unlink-snap", 9124 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 9125 }, 9126 { 9127 op: "remove-profiles:Doing", 9128 name: "some-snap_instance", 9129 revno: snap.R(7), 9130 }, 9131 } 9132 // start with an easier-to-read error if this fails: 9133 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 9134 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 9135 9136 var snapst snapstate.SnapState 9137 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 9138 c.Assert(err, IsNil) 9139 c.Assert(snapst.InstanceKey, Equals, "instance") 9140 c.Assert(snapst.Active, Equals, false) 9141 c.Assert(snapst.AliasesPending, Equals, true) 9142 9143 // the non-parallel instance is still active 9144 snapst = snapstate.SnapState{} 9145 err = snapstate.Get(s.state, "some-snap", &snapst) 9146 c.Assert(err, IsNil) 9147 c.Assert(snapst.InstanceKey, Equals, "") 9148 c.Assert(snapst.Active, Equals, true) 9149 } 9150 9151 type switchScenario struct { 9152 chanFrom string 9153 chanTo string 9154 cohFrom string 9155 cohTo string 9156 summary string 9157 } 9158 9159 var switchScenarios = map[string]switchScenario{ 9160 "no cohort at all": { 9161 chanFrom: "stable", 9162 chanTo: "some-channel", 9163 cohFrom: "", 9164 cohTo: "", 9165 summary: `Switch snap "some-snap" from channel "stable" to "some-channel"`, 9166 }, 9167 "no cohort, from empty channel": { 9168 chanFrom: "", 9169 chanTo: "some-channel", 9170 cohFrom: "", 9171 cohTo: "", 9172 summary: `Switch snap "some-snap" to channel "some-channel"`, 9173 }, 9174 "no cohort change requested": { 9175 chanFrom: "stable", 9176 chanTo: "some-channel", 9177 cohFrom: "some-cohort", 9178 cohTo: "some-cohort", 9179 summary: `Switch snap "some-snap" from channel "stable" to "some-channel"`, 9180 }, 9181 "leave cohort": { 9182 chanFrom: "stable", 9183 chanTo: "stable", 9184 cohFrom: "some-cohort", 9185 cohTo: "", 9186 summary: `Switch snap "some-snap" away from cohort "…me-cohort"`, 9187 }, 9188 "leave cohort, change channel": { 9189 chanFrom: "stable", 9190 chanTo: "edge", 9191 cohFrom: "some-cohort", 9192 cohTo: "", 9193 summary: `Switch snap "some-snap" from channel "stable" to "edge" and away from cohort "…me-cohort"`, 9194 }, 9195 "leave cohort, change from empty channel": { 9196 chanFrom: "", 9197 chanTo: "stable", 9198 cohFrom: "some-cohort", 9199 cohTo: "", 9200 summary: `Switch snap "some-snap" to channel "stable" and away from cohort "…me-cohort"`, 9201 }, 9202 "no channel at all": { 9203 chanFrom: "", 9204 chanTo: "", 9205 cohFrom: "some-cohort", 9206 cohTo: "some-other-cohort", 9207 summary: `Switch snap "some-snap" from cohort "…me-cohort" to "…er-cohort"`, 9208 }, 9209 "no channel change requested": { 9210 chanFrom: "stable", 9211 chanTo: "stable", 9212 cohFrom: "some-cohort", 9213 cohTo: "some-other-cohort", 9214 summary: `Switch snap "some-snap" from cohort "…me-cohort" to "…er-cohort"`, 9215 }, 9216 "no channel change requested, from empty cohort": { 9217 chanFrom: "stable", 9218 chanTo: "stable", 9219 cohFrom: "", 9220 cohTo: "some-cohort", 9221 summary: `Switch snap "some-snap" from no cohort to "…me-cohort"`, 9222 }, 9223 "all change": { 9224 chanFrom: "stable", 9225 chanTo: "edge", 9226 cohFrom: "some-cohort", 9227 cohTo: "some-other-cohort", 9228 summary: `Switch snap "some-snap" from channel "stable" to "edge" and from cohort "…me-cohort" to "…er-cohort"`, 9229 }, 9230 "all change, from empty channel": { 9231 chanFrom: "", 9232 chanTo: "stable", 9233 cohFrom: "some-cohort", 9234 cohTo: "some-other-cohort", 9235 summary: `Switch snap "some-snap" to channel "stable" and from cohort "…me-cohort" to "…er-cohort"`, 9236 }, 9237 "all change, from empty cohort": { 9238 chanFrom: "stable", 9239 chanTo: "edge", 9240 cohFrom: "", 9241 cohTo: "some-cohort", 9242 summary: `Switch snap "some-snap" from channel "stable" to "edge" and from no cohort to "…me-cohort"`, 9243 }, 9244 "all change, from empty channel and cohort": { 9245 chanFrom: "", 9246 chanTo: "stable", 9247 cohFrom: "", 9248 cohTo: "some-cohort", 9249 summary: `Switch snap "some-snap" to channel "stable" and from no cohort to "…me-cohort"`, 9250 }, 9251 "no change": { 9252 chanFrom: "stable", 9253 chanTo: "stable", 9254 cohFrom: "some-cohort", 9255 cohTo: "some-cohort", 9256 summary: `No change switch (no-op)`, 9257 }, 9258 } 9259 9260 func (s *snapmgrTestSuite) TestSwitchScenarios(c *C) { 9261 for k, t := range switchScenarios { 9262 s.testSwitchScenario(c, k, t) 9263 } 9264 } 9265 9266 func (s *snapmgrTestSuite) testSwitchScenario(c *C, desc string, t switchScenario) { 9267 comment := Commentf("%q (%+v)", desc, t) 9268 si := snap.SideInfo{ 9269 RealName: "some-snap", 9270 Revision: snap.R(7), 9271 Channel: t.chanFrom, 9272 SnapID: "foo", 9273 } 9274 9275 s.state.Lock() 9276 defer s.state.Unlock() 9277 9278 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 9279 Sequence: []*snap.SideInfo{&si}, 9280 Current: si.Revision, 9281 Channel: t.chanFrom, 9282 CohortKey: t.cohFrom, 9283 }) 9284 9285 summary := snapstate.SwitchSummary("some-snap", t.chanFrom, t.chanTo, t.cohFrom, t.cohTo) 9286 c.Check(summary, Equals, t.summary, comment) 9287 chg := s.state.NewChange("switch-snap", summary) 9288 ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{ 9289 Channel: t.chanTo, 9290 CohortKey: t.cohTo, 9291 LeaveCohort: t.cohFrom != "" && t.cohTo == "", 9292 }) 9293 c.Assert(err, IsNil, comment) 9294 chg.AddAll(ts) 9295 9296 s.state.Unlock() 9297 s.settle(c) 9298 s.state.Lock() 9299 9300 // switch is not really really doing anything backend related 9301 c.Assert(s.fakeBackend.ops, HasLen, 0, comment) 9302 9303 expectedChanTo := t.chanTo 9304 if t.chanTo == "" { 9305 expectedChanTo = t.chanFrom 9306 } 9307 expectedCohTo := t.cohTo 9308 9309 // ensure the desired channel/cohort has changed 9310 var snapst snapstate.SnapState 9311 err = snapstate.Get(s.state, "some-snap", &snapst) 9312 c.Assert(err, IsNil, comment) 9313 c.Assert(snapst.Channel, Equals, expectedChanTo, comment) 9314 c.Assert(snapst.CohortKey, Equals, expectedCohTo, comment) 9315 9316 // ensure the current info has not changed 9317 info, err := snapst.CurrentInfo() 9318 c.Assert(err, IsNil, comment) 9319 c.Assert(info.Channel, Equals, t.chanFrom, comment) 9320 } 9321 9322 func (s *snapmgrTestSuite) TestUpdateScenarios(c *C) { 9323 // TODO: also use channel-for-7 or equiv to check updates that are switches 9324 for k, t := range switchScenarios { 9325 s.testUpdateScenario(c, k, t) 9326 } 9327 } 9328 9329 func (s *snapmgrTestSuite) testUpdateScenario(c *C, desc string, t switchScenario) { 9330 // reset 9331 s.fakeBackend.ops = nil 9332 9333 comment := Commentf("%q (%+v)", desc, t) 9334 si := snap.SideInfo{ 9335 RealName: "some-snap", 9336 Revision: snap.R(7), 9337 Channel: t.chanFrom, 9338 SnapID: "some-snap-id", 9339 } 9340 9341 s.state.Lock() 9342 defer s.state.Unlock() 9343 9344 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 9345 Sequence: []*snap.SideInfo{&si}, 9346 Active: true, 9347 Current: si.Revision, 9348 Channel: t.chanFrom, 9349 CohortKey: t.cohFrom, 9350 }) 9351 9352 chg := s.state.NewChange("update-snap", t.summary) 9353 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{ 9354 Channel: t.chanTo, 9355 CohortKey: t.cohTo, 9356 LeaveCohort: t.cohFrom != "" && t.cohTo == "", 9357 }, 0, snapstate.Flags{}) 9358 c.Assert(err, IsNil, comment) 9359 chg.AddAll(ts) 9360 9361 s.state.Unlock() 9362 s.settle(c) 9363 s.state.Lock() 9364 9365 // switch is not really really doing anything backend related 9366 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, []string{ 9367 "storesvc-snap-action", 9368 "storesvc-snap-action:action", 9369 "storesvc-download", 9370 "validate-snap:Doing", 9371 "current", 9372 "open-snap-file", 9373 "setup-snap", 9374 "remove-snap-aliases", 9375 "unlink-snap", 9376 "copy-data", 9377 "setup-profiles:Doing", 9378 "candidate", 9379 "link-snap", 9380 "auto-connect:Doing", 9381 "update-aliases", 9382 "cleanup-trash", 9383 }, comment) 9384 9385 expectedChanTo := t.chanTo 9386 if t.chanTo == "" { 9387 expectedChanTo = t.chanFrom 9388 } 9389 expectedCohTo := t.cohTo 9390 9391 // ensure the desired channel/cohort has changed 9392 var snapst snapstate.SnapState 9393 err = snapstate.Get(s.state, "some-snap", &snapst) 9394 c.Assert(err, IsNil, comment) 9395 c.Assert(snapst.Channel, Equals, expectedChanTo, comment) 9396 c.Assert(snapst.CohortKey, Equals, expectedCohTo, comment) 9397 9398 // ensure the current info *has* changed 9399 info, err := snapst.CurrentInfo() 9400 c.Assert(err, IsNil, comment) 9401 c.Assert(info.Channel, Equals, expectedChanTo, comment) 9402 } 9403 9404 func (s *snapmgrTestSuite) TestParallelInstallSwitchRunThrough(c *C) { 9405 si := snap.SideInfo{ 9406 RealName: "some-snap", 9407 Revision: snap.R(7), 9408 Channel: "edge", 9409 SnapID: "foo", 9410 } 9411 9412 s.state.Lock() 9413 defer s.state.Unlock() 9414 9415 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 9416 Sequence: []*snap.SideInfo{&si}, 9417 Current: si.Revision, 9418 Channel: "edge", 9419 }) 9420 9421 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 9422 Sequence: []*snap.SideInfo{&si}, 9423 Current: si.Revision, 9424 Channel: "edge", 9425 InstanceKey: "instance", 9426 }) 9427 9428 chg := s.state.NewChange("switch-snap", "switch snap to some-channel") 9429 ts, err := snapstate.Switch(s.state, "some-snap_instance", &snapstate.RevisionOptions{Channel: "some-channel"}) 9430 c.Assert(err, IsNil) 9431 chg.AddAll(ts) 9432 9433 s.state.Unlock() 9434 defer s.se.Stop() 9435 s.settle(c) 9436 s.state.Lock() 9437 9438 // switch is not really really doing anything backend related 9439 c.Assert(s.fakeBackend.ops, HasLen, 0) 9440 9441 // ensure the desired channel has changed 9442 var snapst snapstate.SnapState 9443 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 9444 c.Assert(err, IsNil) 9445 c.Assert(snapst.Channel, Equals, "some-channel") 9446 9447 // ensure the current info has not changed 9448 info, err := snapst.CurrentInfo() 9449 c.Assert(err, IsNil) 9450 c.Assert(info.Channel, Equals, "edge") 9451 9452 // Ensure that the non-intance snap is unchanged 9453 var nonInstanceSnapst snapstate.SnapState 9454 err = snapstate.Get(s.state, "some-snap", &nonInstanceSnapst) 9455 c.Assert(err, IsNil) 9456 c.Assert(nonInstanceSnapst.Channel, Equals, "edge") 9457 } 9458 9459 func (s *snapmgrTestSuite) TestDisableDoesNotEnableAgain(c *C) { 9460 si := snap.SideInfo{ 9461 RealName: "some-snap", 9462 Revision: snap.R(7), 9463 } 9464 9465 s.state.Lock() 9466 defer s.state.Unlock() 9467 9468 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 9469 Sequence: []*snap.SideInfo{&si}, 9470 Current: snap.R(7), 9471 Active: false, 9472 }) 9473 9474 ts, err := snapstate.Disable(s.state, "some-snap") 9475 c.Assert(err, ErrorMatches, `snap "some-snap" already disabled`) 9476 c.Assert(ts, IsNil) 9477 } 9478 9479 func (s *snapmgrTestSuite) TestUndoMountSnapFailsInCopyData(c *C) { 9480 s.state.Lock() 9481 defer s.state.Unlock() 9482 9483 chg := s.state.NewChange("install", "install a snap") 9484 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 9485 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 9486 c.Assert(err, IsNil) 9487 chg.AddAll(ts) 9488 9489 s.fakeBackend.copySnapDataFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11") 9490 9491 s.state.Unlock() 9492 defer s.se.Stop() 9493 s.settle(c) 9494 s.state.Lock() 9495 9496 expected := fakeOps{ 9497 { 9498 op: "storesvc-snap-action", 9499 userID: 1, 9500 }, 9501 { 9502 op: "storesvc-snap-action:action", 9503 action: store.SnapAction{ 9504 Action: "install", 9505 InstanceName: "some-snap", 9506 Channel: "some-channel", 9507 }, 9508 revno: snap.R(11), 9509 userID: 1, 9510 }, 9511 { 9512 op: "storesvc-download", 9513 name: "some-snap", 9514 }, 9515 { 9516 op: "validate-snap:Doing", 9517 name: "some-snap", 9518 revno: snap.R(11), 9519 }, 9520 { 9521 op: "current", 9522 old: "<no-current>", 9523 }, 9524 { 9525 op: "open-snap-file", 9526 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 9527 sinfo: snap.SideInfo{ 9528 RealName: "some-snap", 9529 SnapID: "some-snap-id", 9530 Channel: "some-channel", 9531 Revision: snap.R(11), 9532 }, 9533 }, 9534 { 9535 op: "setup-snap", 9536 name: "some-snap", 9537 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 9538 revno: snap.R(11), 9539 }, 9540 { 9541 op: "copy-data.failed", 9542 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 9543 old: "<no-old>", 9544 }, 9545 { 9546 op: "remove-snap-data-dir", 9547 name: "some-snap", 9548 path: filepath.Join(dirs.SnapDataDir, "some-snap"), 9549 }, 9550 { 9551 op: "undo-setup-snap", 9552 name: "some-snap", 9553 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 9554 stype: "app", 9555 }, 9556 { 9557 op: "remove-snap-dir", 9558 name: "some-snap", 9559 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 9560 }, 9561 } 9562 // start with an easier-to-read error if this fails: 9563 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 9564 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 9565 } 9566 9567 func (s *snapmgrTestSuite) TestRefreshFailureCausesErrorReport(c *C) { 9568 var errSnap, errMsg, errSig string 9569 var errExtra map[string]string 9570 var n int 9571 restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) { 9572 errSnap = aSnap 9573 errMsg = aErrMsg 9574 errSig = aDupSig 9575 errExtra = extra 9576 n += 1 9577 return "oopsid", nil 9578 }) 9579 defer restore() 9580 9581 si := snap.SideInfo{ 9582 RealName: "some-snap", 9583 SnapID: "some-snap-id", 9584 Revision: snap.R(7), 9585 } 9586 9587 s.state.Lock() 9588 defer s.state.Unlock() 9589 9590 s.state.Set("ubuntu-core-transition-retry", 7) 9591 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 9592 Active: true, 9593 Sequence: []*snap.SideInfo{&si}, 9594 Current: si.Revision, 9595 SnapType: "app", 9596 }) 9597 9598 chg := s.state.NewChange("install", "install a snap") 9599 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 9600 c.Assert(err, IsNil) 9601 chg.AddAll(ts) 9602 9603 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11") 9604 9605 s.state.Unlock() 9606 defer s.se.Stop() 9607 s.settle(c) 9608 s.state.Lock() 9609 9610 // verify we generated a failure report 9611 c.Check(n, Equals, 1) 9612 c.Check(errSnap, Equals, "some-snap") 9613 c.Check(errExtra, DeepEquals, map[string]string{ 9614 "UbuntuCoreTransitionCount": "7", 9615 "Channel": "some-channel", 9616 "Revision": "11", 9617 }) 9618 c.Check(errMsg, Matches, `(?sm)change "install": "install a snap" 9619 prerequisites: Undo 9620 snap-setup: "some-snap" \(11\) "some-channel" 9621 download-snap: Undoing 9622 validate-snap: Done 9623 .* 9624 link-snap: Error 9625 INFO unlink 9626 ERROR fail 9627 auto-connect: Hold 9628 set-auto-aliases: Hold 9629 setup-aliases: Hold 9630 run-hook: Hold 9631 start-snap-services: Hold 9632 cleanup: Hold 9633 run-hook: Hold`) 9634 c.Check(errSig, Matches, `(?sm)snap-install: 9635 prerequisites: Undo 9636 snap-setup: "some-snap" 9637 download-snap: Undoing 9638 validate-snap: Done 9639 .* 9640 link-snap: Error 9641 INFO unlink 9642 ERROR fail 9643 auto-connect: Hold 9644 set-auto-aliases: Hold 9645 setup-aliases: Hold 9646 run-hook: Hold 9647 start-snap-services: Hold 9648 cleanup: Hold 9649 run-hook: Hold`) 9650 9651 // run again with empty "ubuntu-core-transition-retry" 9652 s.state.Set("ubuntu-core-transition-retry", 0) 9653 chg = s.state.NewChange("install", "install a snap") 9654 ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 9655 c.Assert(err, IsNil) 9656 chg.AddAll(ts) 9657 s.state.Unlock() 9658 defer s.se.Stop() 9659 s.settle(c) 9660 s.state.Lock() 9661 // verify that we excluded this field from the bugreport 9662 c.Check(n, Equals, 2) 9663 c.Check(errExtra, DeepEquals, map[string]string{ 9664 "Channel": "some-channel", 9665 "Revision": "11", 9666 }) 9667 9668 } 9669 9670 func (s *snapmgrTestSuite) TestAbortCausesNoErrReport(c *C) { 9671 errReported := 0 9672 restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) { 9673 errReported++ 9674 return "oops-id", nil 9675 }) 9676 defer restore() 9677 9678 s.state.Lock() 9679 defer s.state.Unlock() 9680 9681 chg := s.state.NewChange("install", "install a snap") 9682 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 9683 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 9684 c.Assert(err, IsNil) 9685 9686 s.fakeBackend.linkSnapWaitCh = make(chan int) 9687 s.fakeBackend.linkSnapWaitTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11") 9688 go func() { 9689 <-s.fakeBackend.linkSnapWaitCh 9690 chg.Abort() 9691 s.fakeBackend.linkSnapWaitCh <- 1 9692 }() 9693 9694 chg.AddAll(ts) 9695 9696 s.state.Unlock() 9697 defer s.se.Stop() 9698 s.settle(c) 9699 s.state.Lock() 9700 9701 c.Check(chg.Status(), Equals, state.UndoneStatus) 9702 c.Assert(errReported, Equals, 0) 9703 } 9704 9705 func (s *snapmgrTestSuite) TestErrreportDisable(c *C) { 9706 s.state.Lock() 9707 defer s.state.Unlock() 9708 9709 tr := config.NewTransaction(s.state) 9710 tr.Set("core", "problem-reports.disabled", true) 9711 tr.Commit() 9712 9713 restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) { 9714 c.Fatalf("this should not be reached") 9715 return "", nil 9716 }) 9717 defer restore() 9718 9719 chg := s.state.NewChange("install", "install a snap") 9720 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 9721 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 9722 c.Assert(err, IsNil) 9723 chg.AddAll(ts) 9724 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11") 9725 9726 s.state.Unlock() 9727 defer s.se.Stop() 9728 s.settle(c) 9729 s.state.Lock() 9730 9731 // no failure report was generated 9732 } 9733 9734 func (s *snapmgrTestSuite) TestEnsureRefreshesAtSeedPolicy(c *C) { 9735 // special policy only on classic 9736 r := release.MockOnClassic(true) 9737 defer r() 9738 // set at not seeded yet 9739 st := s.state 9740 st.Lock() 9741 st.Set("seeded", nil) 9742 st.Unlock() 9743 9744 s.snapmgr.Ensure() 9745 9746 st.Lock() 9747 defer st.Unlock() 9748 9749 // check that refresh policies have run in this case 9750 var t1 time.Time 9751 err := st.Get("last-refresh-hints", &t1) 9752 c.Check(err, IsNil) 9753 tr := config.NewTransaction(st) 9754 err = tr.Get("core", "refresh.hold", &t1) 9755 c.Check(err, IsNil) 9756 } 9757 9758 func (s *snapmgrTestSuite) TestEsnureCleansOldSideloads(c *C) { 9759 filenames := func() []string { 9760 filenames, _ := filepath.Glob(filepath.Join(dirs.SnapBlobDir, "*")) 9761 return filenames 9762 } 9763 9764 defer snapstate.MockLocalInstallCleanupWait(200 * time.Millisecond)() 9765 c.Assert(os.MkdirAll(dirs.SnapBlobDir, 0700), IsNil) 9766 // sanity check; note * in go glob matches .foo 9767 c.Assert(filenames(), HasLen, 0) 9768 9769 s0 := filepath.Join(dirs.SnapBlobDir, "some.snap") 9770 s1 := filepath.Join(dirs.SnapBlobDir, dirs.LocalInstallBlobTempPrefix+"-12345") 9771 s2 := filepath.Join(dirs.SnapBlobDir, dirs.LocalInstallBlobTempPrefix+"-67890") 9772 9773 c.Assert(ioutil.WriteFile(s0, nil, 0600), IsNil) 9774 c.Assert(ioutil.WriteFile(s1, nil, 0600), IsNil) 9775 c.Assert(ioutil.WriteFile(s2, nil, 0600), IsNil) 9776 9777 t1 := time.Now() 9778 t0 := t1.Add(-time.Hour) 9779 9780 c.Assert(os.Chtimes(s0, t0, t0), IsNil) 9781 c.Assert(os.Chtimes(s1, t0, t0), IsNil) 9782 c.Assert(os.Chtimes(s2, t1, t1), IsNil) 9783 9784 // all there 9785 c.Assert(filenames(), DeepEquals, []string{s1, s2, s0}) 9786 9787 // set last cleanup in the future 9788 defer snapstate.MockLocalInstallLastCleanup(t1.Add(time.Minute))() 9789 s.snapmgr.Ensure() 9790 // all there ( -> cleanup not done) 9791 c.Assert(filenames(), DeepEquals, []string{s1, s2, s0}) 9792 9793 // set last cleanup to epoch 9794 snapstate.MockLocalInstallLastCleanup(time.Time{}) 9795 9796 s.snapmgr.Ensure() 9797 // oldest sideload gone 9798 c.Assert(filenames(), DeepEquals, []string{s2, s0}) 9799 9800 time.Sleep(200 * time.Millisecond) 9801 9802 s.snapmgr.Ensure() 9803 // all sideloads gone 9804 c.Assert(filenames(), DeepEquals, []string{s0}) 9805 9806 } 9807 9808 func (s *snapmgrTestSuite) verifyRefreshLast(c *C) { 9809 var lastRefresh time.Time 9810 9811 s.state.Get("last-refresh", &lastRefresh) 9812 c.Check(time.Now().Year(), Equals, lastRefresh.Year()) 9813 } 9814 9815 func makeTestRefreshConfig(st *state.State) { 9816 // avoid special at seed policy 9817 now := time.Now() 9818 st.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, now.Location())) 9819 9820 tr := config.NewTransaction(st) 9821 tr.Set("core", "refresh.timer", "00:00-23:59") 9822 tr.Commit() 9823 } 9824 9825 func (s *snapmgrTestSuite) TestEnsureRefreshRefusesLegacyWeekdaySchedules(c *C) { 9826 s.state.Lock() 9827 defer s.state.Unlock() 9828 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 9829 9830 logbuf, restore := logger.MockLogger() 9831 defer restore() 9832 9833 s.state.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, time.UTC)) 9834 tr := config.NewTransaction(s.state) 9835 tr.Set("core", "refresh.timer", "") 9836 tr.Set("core", "refresh.schedule", "00:00-23:59/mon@12:00-14:00") 9837 tr.Commit() 9838 9839 // Ensure() also runs ensureRefreshes() 9840 s.state.Unlock() 9841 s.se.Ensure() 9842 s.state.Lock() 9843 9844 c.Check(logbuf.String(), testutil.Contains, `cannot use refresh.schedule configuration: cannot parse "mon@12:00": not a valid time`) 9845 schedule, legacy, err := s.snapmgr.RefreshSchedule() 9846 c.Assert(err, IsNil) 9847 c.Check(schedule, Equals, "00:00~24:00/4") 9848 c.Check(legacy, Equals, false) 9849 9850 tr = config.NewTransaction(s.state) 9851 refreshTimer := "canary" 9852 refreshSchedule := "canary" 9853 c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil) 9854 c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil) 9855 c.Check(refreshTimer, Equals, "") 9856 c.Check(refreshSchedule, Equals, "00:00-23:59/mon@12:00-14:00") 9857 } 9858 9859 func (s *snapmgrTestSuite) TestEnsureRefreshLegacyScheduleIsLowerPriority(c *C) { 9860 s.state.Lock() 9861 defer s.state.Unlock() 9862 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 9863 9864 s.state.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, time.UTC)) 9865 tr := config.NewTransaction(s.state) 9866 tr.Set("core", "refresh.timer", "00:00-23:59,,mon,12:00-14:00") 9867 // legacy schedule is invalid 9868 tr.Set("core", "refresh.schedule", "00:00-23:59/mon@12:00-14:00") 9869 tr.Commit() 9870 9871 // Ensure() also runs ensureRefreshes() 9872 s.state.Unlock() 9873 s.se.Ensure() 9874 s.state.Lock() 9875 9876 // expecting new refresh.timer to have been used, fallback to legacy was 9877 // not attempted otherwise it would get reset to the default due to 9878 // refresh.schedule being garbage 9879 schedule, legacy, err := s.snapmgr.RefreshSchedule() 9880 c.Assert(err, IsNil) 9881 c.Check(schedule, Equals, "00:00-23:59,,mon,12:00-14:00") 9882 c.Check(legacy, Equals, false) 9883 } 9884 9885 func (s *snapmgrTestSuite) TestEnsureRefreshFallbackToLegacySchedule(c *C) { 9886 s.state.Lock() 9887 defer s.state.Unlock() 9888 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 9889 9890 tr := config.NewTransaction(s.state) 9891 tr.Set("core", "refresh.timer", "") 9892 tr.Set("core", "refresh.schedule", "00:00-23:59") 9893 tr.Commit() 9894 9895 // Ensure() also runs ensureRefreshes() 9896 s.state.Unlock() 9897 s.se.Ensure() 9898 s.state.Lock() 9899 9900 // refresh.timer is unset, triggering automatic fallback to legacy 9901 // schedule if that was set 9902 schedule, legacy, err := s.snapmgr.RefreshSchedule() 9903 c.Assert(err, IsNil) 9904 c.Check(schedule, Equals, "00:00-23:59") 9905 c.Check(legacy, Equals, true) 9906 } 9907 9908 func (s *snapmgrTestSuite) TestEnsureRefreshFallbackToDefaultOnError(c *C) { 9909 s.state.Lock() 9910 defer s.state.Unlock() 9911 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 9912 9913 tr := config.NewTransaction(s.state) 9914 tr.Set("core", "refresh.timer", "garbage-in") 9915 tr.Set("core", "refresh.schedule", "00:00-23:59") 9916 tr.Commit() 9917 9918 // Ensure() also runs ensureRefreshes() 9919 s.state.Unlock() 9920 s.se.Ensure() 9921 s.state.Lock() 9922 9923 // automatic fallback to default schedule if refresh.timer is set but 9924 // cannot be parsed 9925 schedule, legacy, err := s.snapmgr.RefreshSchedule() 9926 c.Assert(err, IsNil) 9927 c.Check(schedule, Equals, "00:00~24:00/4") 9928 c.Check(legacy, Equals, false) 9929 9930 tr = config.NewTransaction(s.state) 9931 refreshTimer := "canary" 9932 refreshSchedule := "canary" 9933 c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil) 9934 c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil) 9935 c.Check(refreshTimer, Equals, "garbage-in") 9936 c.Check(refreshSchedule, Equals, "00:00-23:59") 9937 } 9938 9939 func (s *snapmgrTestSuite) TestEnsureRefreshFallbackOnEmptyToDefaultSchedule(c *C) { 9940 s.state.Lock() 9941 defer s.state.Unlock() 9942 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 9943 9944 tr := config.NewTransaction(s.state) 9945 tr.Set("core", "refresh.timer", "") 9946 tr.Set("core", "refresh.schedule", "") 9947 tr.Commit() 9948 9949 // Ensure() also runs ensureRefreshes() 9950 s.state.Unlock() 9951 s.se.Ensure() 9952 s.state.Lock() 9953 9954 // automatic fallback to default schedule if neither refresh.timer nor 9955 // refresh.schedule was set 9956 schedule, legacy, err := s.snapmgr.RefreshSchedule() 9957 c.Assert(err, IsNil) 9958 c.Check(schedule, Equals, "00:00~24:00/4") 9959 c.Check(legacy, Equals, false) 9960 9961 tr = config.NewTransaction(s.state) 9962 refreshTimer := "canary" 9963 refreshSchedule := "canary" 9964 c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil) 9965 c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil) 9966 c.Check(refreshTimer, Equals, "") 9967 c.Check(refreshSchedule, Equals, "") 9968 } 9969 9970 func (s *snapmgrTestSuite) TestEnsureRefreshesNoUpdate(c *C) { 9971 s.state.Lock() 9972 defer s.state.Unlock() 9973 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 9974 9975 makeTestRefreshConfig(s.state) 9976 9977 // Ensure() also runs ensureRefreshes() 9978 s.state.Unlock() 9979 s.snapmgr.Ensure() 9980 s.state.Lock() 9981 9982 // nothing needs to be done, but last-refresh got updated 9983 c.Check(s.state.Changes(), HasLen, 0) 9984 s.verifyRefreshLast(c) 9985 9986 // ensure the next-refresh time is reset and re-calculated 9987 c.Check(s.snapmgr.NextRefresh().IsZero(), Equals, true) 9988 } 9989 9990 func (s *snapmgrTestSuite) TestEnsureRefreshesAlreadyRanInThisInterval(c *C) { 9991 s.state.Lock() 9992 defer s.state.Unlock() 9993 9994 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { 9995 return true, nil 9996 } 9997 nextRefresh := s.snapmgr.NextRefresh() 9998 c.Check(nextRefresh.IsZero(), Equals, true) 9999 10000 now := time.Now() 10001 fakeLastRefresh := now.Add(-1 * time.Hour) 10002 s.state.Set("last-refresh", fakeLastRefresh) 10003 10004 tr := config.NewTransaction(s.state) 10005 tr.Set("core", "refresh.timer", fmt.Sprintf("00:00-%02d:%02d", now.Hour(), now.Minute())) 10006 tr.Commit() 10007 10008 // Ensure() also runs ensureRefreshes() 10009 s.state.Unlock() 10010 s.snapmgr.Ensure() 10011 s.state.Lock() 10012 10013 // nothing needs to be done and no refresh was run 10014 c.Check(s.state.Changes(), HasLen, 0) 10015 10016 var refreshLast time.Time 10017 s.state.Get("last-refresh", &refreshLast) 10018 c.Check(refreshLast.Equal(fakeLastRefresh), Equals, true) 10019 10020 // but a nextRefresh time got calculated 10021 nextRefresh = s.snapmgr.NextRefresh() 10022 c.Check(nextRefresh.IsZero(), Equals, false) 10023 10024 // run ensure again to test that nextRefresh again to ensure that 10025 // nextRefresh is not calculated again if nothing changes 10026 s.state.Unlock() 10027 s.snapmgr.Ensure() 10028 s.state.Lock() 10029 c.Check(s.snapmgr.NextRefresh(), Equals, nextRefresh) 10030 } 10031 10032 func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdate(c *C) { 10033 s.state.Lock() 10034 defer s.state.Unlock() 10035 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 10036 10037 makeTestRefreshConfig(s.state) 10038 10039 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 10040 Active: true, 10041 Sequence: []*snap.SideInfo{ 10042 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 10043 }, 10044 Current: snap.R(1), 10045 SnapType: "app", 10046 }) 10047 10048 // Ensure() also runs ensureRefreshes() and our test setup has an 10049 // update for the "some-snap" in our fake store 10050 s.state.Unlock() 10051 s.snapmgr.Ensure() 10052 s.state.Lock() 10053 10054 // verify we have an auto-refresh change scheduled now 10055 c.Assert(s.state.Changes(), HasLen, 1) 10056 chg := s.state.Changes()[0] 10057 c.Check(chg.Kind(), Equals, "auto-refresh") 10058 c.Check(chg.IsReady(), Equals, false) 10059 s.verifyRefreshLast(c) 10060 10061 checkIsAutoRefresh(c, chg.Tasks(), true) 10062 } 10063 10064 func (s *snapmgrTestSuite) TestEnsureRefreshesImmediateWithUpdate(c *C) { 10065 r := release.MockOnClassic(false) 10066 defer r() 10067 10068 s.state.Lock() 10069 defer s.state.Unlock() 10070 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 10071 10072 // lastRefresh is unset/zero => immediate refresh try 10073 10074 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 10075 Active: true, 10076 Sequence: []*snap.SideInfo{ 10077 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 10078 }, 10079 Current: snap.R(1), 10080 SnapType: "app", 10081 }) 10082 10083 // Ensure() also runs ensureRefreshes() and our test setup has an 10084 // update for the "some-snap" in our fake store 10085 s.state.Unlock() 10086 s.snapmgr.Ensure() 10087 s.state.Lock() 10088 10089 // verify we have an auto-refresh change scheduled now 10090 c.Assert(s.state.Changes(), HasLen, 1) 10091 chg := s.state.Changes()[0] 10092 c.Check(chg.Kind(), Equals, "auto-refresh") 10093 c.Check(chg.IsReady(), Equals, false) 10094 s.verifyRefreshLast(c) 10095 } 10096 10097 func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdateError(c *C) { 10098 s.state.Lock() 10099 defer s.state.Unlock() 10100 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 10101 10102 makeTestRefreshConfig(s.state) 10103 10104 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 10105 Active: true, 10106 Sequence: []*snap.SideInfo{ 10107 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 10108 }, 10109 Current: snap.R(1), 10110 SnapType: "app", 10111 }) 10112 10113 // Ensure() also runs ensureRefreshes() and our test setup has an 10114 // update for the "some-snap" in our fake store 10115 s.state.Unlock() 10116 s.snapmgr.Ensure() 10117 s.state.Lock() 10118 10119 c.Check(s.state.Changes(), HasLen, 1) 10120 chg := s.state.Changes()[0] 10121 terr := s.state.NewTask("error-trigger", "simulate an error") 10122 tasks := chg.Tasks() 10123 for _, t := range tasks[:len(tasks)-2] { 10124 terr.WaitFor(t) 10125 } 10126 chg.AddTask(terr) 10127 10128 // run the changes 10129 s.state.Unlock() 10130 s.settle(c) 10131 s.state.Lock() 10132 10133 s.verifyRefreshLast(c) 10134 } 10135 10136 func (s *snapmgrTestSuite) TestEnsureRefreshesInFlight(c *C) { 10137 s.state.Lock() 10138 defer s.state.Unlock() 10139 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 10140 10141 makeTestRefreshConfig(s.state) 10142 10143 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 10144 Active: true, 10145 Sequence: []*snap.SideInfo{ 10146 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 10147 }, 10148 Current: snap.R(1), 10149 SnapType: "app", 10150 }) 10151 10152 // simulate an in-flight change 10153 chg := s.state.NewChange("auto-refresh", "...") 10154 chg.SetStatus(state.DoStatus) 10155 c.Check(s.state.Changes(), HasLen, 1) 10156 10157 s.state.Unlock() 10158 s.snapmgr.Ensure() 10159 s.state.Lock() 10160 10161 // verify no additional change got generated 10162 c.Check(s.state.Changes(), HasLen, 1) 10163 } 10164 10165 func mockAutoRefreshAssertions(f func(st *state.State, userID int) error) func() { 10166 origAutoRefreshAssertions := snapstate.AutoRefreshAssertions 10167 snapstate.AutoRefreshAssertions = f 10168 return func() { 10169 snapstate.AutoRefreshAssertions = origAutoRefreshAssertions 10170 } 10171 } 10172 10173 func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdateStoreError(c *C) { 10174 s.state.Lock() 10175 defer s.state.Unlock() 10176 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 10177 10178 // avoid special at seed policy 10179 s.state.Set("last-refresh", time.Time{}) 10180 autoRefreshAssertionsCalled := 0 10181 restore := mockAutoRefreshAssertions(func(st *state.State, userID int) error { 10182 // simulate failure in snapstate.AutoRefresh() 10183 autoRefreshAssertionsCalled++ 10184 return fmt.Errorf("simulate store error") 10185 }) 10186 defer restore() 10187 10188 // check that no change got created and that autoRefreshAssertins 10189 // got called once 10190 s.state.Unlock() 10191 s.snapmgr.Ensure() 10192 s.state.Lock() 10193 c.Check(s.state.Changes(), HasLen, 0) 10194 c.Check(autoRefreshAssertionsCalled, Equals, 1) 10195 10196 // run Ensure() again and check that AutoRefresh() did not run 10197 // again because to test that lastRefreshAttempt backoff is working 10198 s.state.Unlock() 10199 s.snapmgr.Ensure() 10200 s.state.Lock() 10201 c.Check(s.state.Changes(), HasLen, 0) 10202 c.Check(autoRefreshAssertionsCalled, Equals, 1) 10203 } 10204 10205 func (s *snapmgrTestSuite) testEnsureRefreshesDisabledViaSnapdControl(c *C, confSet func(*config.Transaction)) { 10206 st := s.state 10207 st.Lock() 10208 defer st.Unlock() 10209 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 10210 10211 makeTestRefreshConfig(st) 10212 10213 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 10214 Active: true, 10215 Sequence: []*snap.SideInfo{ 10216 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 10217 }, 10218 Current: snap.R(1), 10219 SnapType: "app", 10220 }) 10221 10222 // snapstate.AutoRefresh is called from AutoRefresh() 10223 autoRefreshAssertionsCalled := 0 10224 restore := mockAutoRefreshAssertions(func(st *state.State, userID int) error { 10225 autoRefreshAssertionsCalled++ 10226 return nil 10227 }) 10228 defer restore() 10229 10230 // pretend the device is refresh-control: managed 10231 oldCanManageRefreshes := snapstate.CanManageRefreshes 10232 snapstate.CanManageRefreshes = func(*state.State) bool { 10233 return true 10234 } 10235 defer func() { snapstate.CanManageRefreshes = oldCanManageRefreshes }() 10236 10237 tr := config.NewTransaction(st) 10238 confSet(tr) 10239 tr.Commit() 10240 10241 // Ensure() also runs ensureRefreshes() 10242 st.Unlock() 10243 s.snapmgr.Ensure() 10244 st.Lock() 10245 10246 // no refresh was called (i.e. no update to last-refresh) 10247 var lastRefresh time.Time 10248 st.Get("last-refresh", &lastRefresh) 10249 c.Check(lastRefresh.Year(), Equals, 2009) 10250 10251 // AutoRefresh was not called 10252 c.Check(autoRefreshAssertionsCalled, Equals, 0) 10253 10254 // The last refresh hints got updated 10255 var lastRefreshHints time.Time 10256 st.Get("last-refresh-hints", &lastRefreshHints) 10257 c.Check(lastRefreshHints.Year(), Equals, time.Now().Year()) 10258 } 10259 10260 func (s *snapmgrTestSuite) TestEnsureRefreshDisableLegacy(c *C) { 10261 f := func(tr *config.Transaction) { 10262 tr.Set("core", "refresh.timer", "") 10263 tr.Set("core", "refresh.schedule", "managed") 10264 } 10265 s.testEnsureRefreshesDisabledViaSnapdControl(c, f) 10266 } 10267 10268 func (s *snapmgrTestSuite) TestEnsureRefreshDisableNew(c *C) { 10269 f := func(tr *config.Transaction) { 10270 tr.Set("core", "refresh.timer", "managed") 10271 tr.Set("core", "refresh.schedule", "") 10272 } 10273 s.testEnsureRefreshesDisabledViaSnapdControl(c, f) 10274 } 10275 10276 func (s *snapmgrTestSuite) TestEnsureRefreshDisableNewTrumpsOld(c *C) { 10277 f := func(tr *config.Transaction) { 10278 tr.Set("core", "refresh.timer", "managed") 10279 tr.Set("core", "refresh.schedule", "00:00-12:00") 10280 } 10281 s.testEnsureRefreshesDisabledViaSnapdControl(c, f) 10282 } 10283 10284 func (s *snapmgrTestSuite) TestDefaultRefreshScheduleParsing(c *C) { 10285 l, err := timeutil.ParseSchedule(snapstate.DefaultRefreshSchedule) 10286 c.Assert(err, IsNil) 10287 c.Assert(l, HasLen, 1) 10288 } 10289 10290 func (s *snapmgrTestSuite) TestWaitRestartBasics(c *C) { 10291 r := release.MockOnClassic(true) 10292 defer r() 10293 10294 st := s.state 10295 st.Lock() 10296 defer st.Unlock() 10297 10298 task := st.NewTask("auto-connect", "...") 10299 10300 // not restarting 10301 state.MockRestarting(st, state.RestartUnset) 10302 si := &snap.SideInfo{RealName: "some-app"} 10303 snaptest.MockSnap(c, "name: some-app\nversion: 1", si) 10304 snapsup := &snapstate.SnapSetup{SideInfo: si} 10305 err := snapstate.WaitRestart(task, snapsup) 10306 c.Check(err, IsNil) 10307 10308 // restarting ... we always wait 10309 state.MockRestarting(st, state.RestartDaemon) 10310 err = snapstate.WaitRestart(task, snapsup) 10311 c.Check(err, FitsTypeOf, &state.Retry{}) 10312 } 10313 10314 type snapmgrQuerySuite struct { 10315 st *state.State 10316 restore func() 10317 } 10318 10319 var _ = Suite(&snapmgrQuerySuite{}) 10320 10321 func (s *snapmgrQuerySuite) SetUpTest(c *C) { 10322 st := state.New(nil) 10323 st.Lock() 10324 defer st.Unlock() 10325 10326 restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 10327 s.restore = func() { 10328 restoreSanitize() 10329 } 10330 10331 s.st = st 10332 10333 dirs.SetRootDir(c.MkDir()) 10334 10335 // Write a snap.yaml with fake name 10336 sideInfo11 := &snap.SideInfo{RealName: "name1", Revision: snap.R(11), EditedSummary: "s11", SnapID: "123123123"} 10337 sideInfo12 := &snap.SideInfo{RealName: "name1", Revision: snap.R(12), EditedSummary: "s12", SnapID: "123123123"} 10338 instanceSideInfo13 := &snap.SideInfo{RealName: "name1", Revision: snap.R(13), EditedSummary: "s13 instance", SnapID: "123123123"} 10339 snaptest.MockSnap(c, ` 10340 name: name0 10341 version: 1.1 10342 description: | 10343 Lots of text`, sideInfo11) 10344 snaptest.MockSnap(c, ` 10345 name: name0 10346 version: 1.2 10347 description: | 10348 Lots of text`, sideInfo12) 10349 snaptest.MockSnapInstance(c, "name1_instance", ` 10350 name: name0 10351 version: 1.3 10352 description: | 10353 Lots of text`, instanceSideInfo13) 10354 snapstate.Set(st, "name1", &snapstate.SnapState{ 10355 Active: true, 10356 Sequence: []*snap.SideInfo{sideInfo11, sideInfo12}, 10357 Current: sideInfo12.Revision, 10358 SnapType: "app", 10359 }) 10360 snapstate.Set(st, "name1_instance", &snapstate.SnapState{ 10361 Active: true, 10362 Sequence: []*snap.SideInfo{instanceSideInfo13}, 10363 Current: instanceSideInfo13.Revision, 10364 SnapType: "app", 10365 InstanceKey: "instance", 10366 }) 10367 10368 // have also a snap being installed 10369 /* 10370 snapstate.Set(st, "installing", &snapstate.SnapState{ 10371 Candidate: &snap.SideInfo{RealName: "installing", Revision: snap.R(1)}, 10372 }) 10373 */ 10374 } 10375 10376 func (s *snapmgrQuerySuite) TearDownTest(c *C) { 10377 dirs.SetRootDir("") 10378 s.restore() 10379 } 10380 10381 func (s *snapmgrQuerySuite) TestInfo(c *C) { 10382 st := s.st 10383 st.Lock() 10384 defer st.Unlock() 10385 10386 info, err := snapstate.Info(st, "name1", snap.R(11)) 10387 c.Assert(err, IsNil) 10388 10389 c.Check(info.InstanceName(), Equals, "name1") 10390 c.Check(info.Revision, Equals, snap.R(11)) 10391 c.Check(info.Summary(), Equals, "s11") 10392 c.Check(info.Version, Equals, "1.1") 10393 c.Check(info.Description(), Equals, "Lots of text") 10394 } 10395 10396 func (s *snapmgrQuerySuite) TestSnapStateCurrentInfo(c *C) { 10397 st := s.st 10398 st.Lock() 10399 defer st.Unlock() 10400 10401 var snapst snapstate.SnapState 10402 err := snapstate.Get(st, "name1", &snapst) 10403 c.Assert(err, IsNil) 10404 10405 info, err := snapst.CurrentInfo() 10406 c.Assert(err, IsNil) 10407 10408 c.Check(info.InstanceName(), Equals, "name1") 10409 c.Check(info.Revision, Equals, snap.R(12)) 10410 c.Check(info.Summary(), Equals, "s12") 10411 c.Check(info.Version, Equals, "1.2") 10412 c.Check(info.Description(), Equals, "Lots of text") 10413 c.Check(info.Media, IsNil) 10414 } 10415 10416 func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoLoadsAuxiliaryStoreInfo(c *C) { 10417 storeInfo := &snapstate.AuxStoreInfo{Media: snap.MediaInfos{ 10418 { 10419 Type: "icon", 10420 URL: "http://example.com/favicon.ico", 10421 }, 10422 }} 10423 10424 c.Assert(snapstate.KeepAuxStoreInfo("123123123", storeInfo), IsNil) 10425 10426 st := s.st 10427 st.Lock() 10428 defer st.Unlock() 10429 10430 var snapst snapstate.SnapState 10431 err := snapstate.Get(st, "name1", &snapst) 10432 c.Assert(err, IsNil) 10433 10434 info, err := snapst.CurrentInfo() 10435 c.Assert(err, IsNil) 10436 10437 c.Check(info.InstanceName(), Equals, "name1") 10438 c.Check(info.Revision, Equals, snap.R(12)) 10439 c.Check(info.Summary(), Equals, "s12") 10440 c.Check(info.Version, Equals, "1.2") 10441 c.Check(info.Description(), Equals, "Lots of text") 10442 c.Check(info.Media, DeepEquals, storeInfo.Media) 10443 } 10444 10445 func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoParallelInstall(c *C) { 10446 st := s.st 10447 st.Lock() 10448 defer st.Unlock() 10449 10450 var snapst snapstate.SnapState 10451 err := snapstate.Get(st, "name1_instance", &snapst) 10452 c.Assert(err, IsNil) 10453 10454 info, err := snapst.CurrentInfo() 10455 c.Assert(err, IsNil) 10456 10457 c.Check(info.InstanceName(), Equals, "name1_instance") 10458 c.Check(info.Revision, Equals, snap.R(13)) 10459 c.Check(info.Summary(), Equals, "s13 instance") 10460 c.Check(info.Version, Equals, "1.3") 10461 c.Check(info.Description(), Equals, "Lots of text") 10462 } 10463 10464 func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoErrNoCurrent(c *C) { 10465 snapst := new(snapstate.SnapState) 10466 _, err := snapst.CurrentInfo() 10467 c.Assert(err, Equals, snapstate.ErrNoCurrent) 10468 10469 } 10470 10471 func (s *snapmgrQuerySuite) TestCurrentInfo(c *C) { 10472 st := s.st 10473 st.Lock() 10474 defer st.Unlock() 10475 10476 info, err := snapstate.CurrentInfo(st, "name1") 10477 c.Assert(err, IsNil) 10478 10479 c.Check(info.InstanceName(), Equals, "name1") 10480 c.Check(info.Revision, Equals, snap.R(12)) 10481 } 10482 10483 func (s *snapmgrQuerySuite) TestCurrentInfoAbsent(c *C) { 10484 st := s.st 10485 st.Lock() 10486 defer st.Unlock() 10487 10488 _, err := snapstate.CurrentInfo(st, "absent") 10489 c.Assert(err, ErrorMatches, `snap "absent" is not installed`) 10490 } 10491 10492 func (s *snapmgrQuerySuite) TestActiveInfos(c *C) { 10493 st := s.st 10494 st.Lock() 10495 defer st.Unlock() 10496 10497 infos, err := snapstate.ActiveInfos(st) 10498 c.Assert(err, IsNil) 10499 10500 c.Check(infos, HasLen, 2) 10501 10502 instanceName := "name1_instance" 10503 if infos[0].InstanceName() != instanceName && infos[1].InstanceName() != instanceName { 10504 c.Fail() 10505 } 10506 // need stable ordering 10507 if infos[0].InstanceName() == instanceName { 10508 infos[1], infos[0] = infos[0], infos[1] 10509 } 10510 10511 c.Check(infos[0].InstanceName(), Equals, "name1") 10512 c.Check(infos[0].Revision, Equals, snap.R(12)) 10513 c.Check(infos[0].Summary(), Equals, "s12") 10514 c.Check(infos[0].Version, Equals, "1.2") 10515 c.Check(infos[0].Description(), Equals, "Lots of text") 10516 10517 c.Check(infos[1].InstanceName(), Equals, "name1_instance") 10518 c.Check(infos[1].Revision, Equals, snap.R(13)) 10519 c.Check(infos[1].Summary(), Equals, "s13 instance") 10520 c.Check(infos[1].Version, Equals, "1.3") 10521 c.Check(infos[1].Description(), Equals, "Lots of text") 10522 } 10523 10524 func (s *snapmgrQuerySuite) TestGadgetInfo(c *C) { 10525 st := s.st 10526 st.Lock() 10527 defer st.Unlock() 10528 10529 deviceCtxNoGadget := deviceWithoutGadgetContext() 10530 deviceCtx := deviceWithGadgetContext("gadget") 10531 10532 _, err := snapstate.GadgetInfo(st, deviceCtxNoGadget) 10533 c.Assert(err, Equals, state.ErrNoState) 10534 10535 _, err = snapstate.GadgetInfo(st, deviceCtx) 10536 c.Assert(err, Equals, state.ErrNoState) 10537 10538 sideInfo := &snap.SideInfo{ 10539 RealName: "gadget", 10540 Revision: snap.R(2), 10541 } 10542 snaptest.MockSnap(c, ` 10543 name: gadget 10544 type: gadget 10545 version: v1 10546 `, sideInfo) 10547 snapstate.Set(st, "gadget", &snapstate.SnapState{ 10548 SnapType: "gadget", 10549 Active: true, 10550 Sequence: []*snap.SideInfo{sideInfo}, 10551 Current: sideInfo.Revision, 10552 }) 10553 10554 info, err := snapstate.GadgetInfo(st, deviceCtx) 10555 c.Assert(err, IsNil) 10556 10557 c.Check(info.InstanceName(), Equals, "gadget") 10558 c.Check(info.Revision, Equals, snap.R(2)) 10559 c.Check(info.Version, Equals, "v1") 10560 c.Check(info.GetType(), Equals, snap.TypeGadget) 10561 } 10562 10563 func (s *snapmgrQuerySuite) TestCoreInfoInternal(c *C) { 10564 st := s.st 10565 st.Lock() 10566 defer st.Unlock() 10567 10568 for testNr, t := range []struct { 10569 expectedSnap string 10570 snapNames []string 10571 errMatcher string 10572 }{ 10573 // nothing 10574 {"", []string{}, state.ErrNoState.Error()}, 10575 // single 10576 {"core", []string{"core"}, ""}, 10577 {"ubuntu-core", []string{"ubuntu-core"}, ""}, 10578 {"hard-core", []string{"hard-core"}, ""}, 10579 // unrolled loop to ensure we don't pass because 10580 // the order is randomly right 10581 {"core", []string{"core", "ubuntu-core"}, ""}, 10582 {"core", []string{"core", "ubuntu-core"}, ""}, 10583 {"core", []string{"core", "ubuntu-core"}, ""}, 10584 {"core", []string{"core", "ubuntu-core"}, ""}, 10585 {"core", []string{"core", "ubuntu-core"}, ""}, 10586 {"core", []string{"core", "ubuntu-core"}, ""}, 10587 {"core", []string{"core", "ubuntu-core"}, ""}, 10588 {"core", []string{"core", "ubuntu-core"}, ""}, 10589 // unknown combination 10590 {"", []string{"duo-core", "single-core"}, `unexpected cores.*`}, 10591 // multi-core is not supported 10592 {"", []string{"core", "ubuntu-core", "multi-core"}, `unexpected number of cores, got 3`}, 10593 } { 10594 // clear snapstate 10595 st.Set("snaps", map[string]*json.RawMessage{}) 10596 10597 for _, snapName := range t.snapNames { 10598 sideInfo := &snap.SideInfo{ 10599 RealName: snapName, 10600 Revision: snap.R(1), 10601 } 10602 snaptest.MockSnap(c, fmt.Sprintf("name: %q\ntype: os\nversion: %q\n", snapName, snapName), sideInfo) 10603 snapstate.Set(st, snapName, &snapstate.SnapState{ 10604 SnapType: string(snap.TypeOS), 10605 Active: true, 10606 Sequence: []*snap.SideInfo{sideInfo}, 10607 Current: sideInfo.Revision, 10608 }) 10609 } 10610 10611 info, err := snapstate.CoreInfoInternal(st) 10612 if t.errMatcher != "" { 10613 c.Assert(err, ErrorMatches, t.errMatcher) 10614 } else { 10615 c.Assert(info, NotNil) 10616 c.Check(info.InstanceName(), Equals, t.expectedSnap, Commentf("(%d) test %q %v", testNr, t.expectedSnap, t.snapNames)) 10617 c.Check(info.GetType(), Equals, snap.TypeOS) 10618 } 10619 } 10620 } 10621 10622 func (s *snapmgrQuerySuite) TestHasSnapOfType(c *C) { 10623 st := s.st 10624 st.Lock() 10625 defer st.Unlock() 10626 10627 // an app snap is already setup 10628 ok, err := snapstate.HasSnapOfType(st, snap.TypeApp) 10629 c.Assert(err, IsNil) 10630 c.Check(ok, Equals, true) 10631 10632 for _, x := range []struct { 10633 snapName string 10634 snapType snap.Type 10635 }{ 10636 { 10637 snapName: "gadget", 10638 snapType: snap.TypeGadget, 10639 }, 10640 { 10641 snapName: "core", 10642 snapType: snap.TypeOS, 10643 }, 10644 { 10645 snapName: "kernel", 10646 snapType: snap.TypeKernel, 10647 }, 10648 { 10649 snapName: "base", 10650 snapType: snap.TypeBase, 10651 }, 10652 } { 10653 ok, err := snapstate.HasSnapOfType(st, x.snapType) 10654 c.Assert(err, IsNil) 10655 c.Check(ok, Equals, false, Commentf("%q", x.snapType)) 10656 10657 sideInfo := &snap.SideInfo{ 10658 RealName: x.snapName, 10659 Revision: snap.R(2), 10660 } 10661 snapstate.Set(st, x.snapName, &snapstate.SnapState{ 10662 SnapType: string(x.snapType), 10663 Active: true, 10664 Sequence: []*snap.SideInfo{sideInfo}, 10665 Current: sideInfo.Revision, 10666 }) 10667 10668 ok, err = snapstate.HasSnapOfType(st, x.snapType) 10669 c.Assert(err, IsNil) 10670 c.Check(ok, Equals, true) 10671 } 10672 } 10673 10674 func (s *snapmgrQuerySuite) TestPreviousSideInfo(c *C) { 10675 st := s.st 10676 st.Lock() 10677 defer st.Unlock() 10678 10679 var snapst snapstate.SnapState 10680 err := snapstate.Get(st, "name1", &snapst) 10681 c.Assert(err, IsNil) 10682 c.Assert(snapst.CurrentSideInfo(), NotNil) 10683 c.Assert(snapst.CurrentSideInfo().Revision, Equals, snap.R(12)) 10684 c.Assert(snapstate.PreviousSideInfo(&snapst), NotNil) 10685 c.Assert(snapstate.PreviousSideInfo(&snapst).Revision, Equals, snap.R(11)) 10686 } 10687 10688 func (s *snapmgrQuerySuite) TestPreviousSideInfoNoCurrent(c *C) { 10689 st := s.st 10690 st.Lock() 10691 defer st.Unlock() 10692 10693 snapst := &snapstate.SnapState{} 10694 c.Assert(snapstate.PreviousSideInfo(snapst), IsNil) 10695 } 10696 10697 func (s *snapmgrQuerySuite) TestAll(c *C) { 10698 st := s.st 10699 st.Lock() 10700 defer st.Unlock() 10701 10702 snapStates, err := snapstate.All(st) 10703 c.Assert(err, IsNil) 10704 c.Assert(snapStates, HasLen, 2) 10705 10706 n, err := snapstate.NumSnaps(st) 10707 c.Assert(err, IsNil) 10708 c.Check(n, Equals, 2) 10709 10710 snapst := snapStates["name1"] 10711 c.Assert(snapst, NotNil) 10712 10713 c.Check(snapst.Active, Equals, true) 10714 c.Check(snapst.CurrentSideInfo(), NotNil) 10715 10716 info12, err := snap.ReadInfo("name1", snapst.CurrentSideInfo()) 10717 c.Assert(err, IsNil) 10718 10719 c.Check(info12.InstanceName(), Equals, "name1") 10720 c.Check(info12.Revision, Equals, snap.R(12)) 10721 c.Check(info12.Summary(), Equals, "s12") 10722 c.Check(info12.Version, Equals, "1.2") 10723 c.Check(info12.Description(), Equals, "Lots of text") 10724 10725 info11, err := snap.ReadInfo("name1", snapst.Sequence[0]) 10726 c.Assert(err, IsNil) 10727 10728 c.Check(info11.InstanceName(), Equals, "name1") 10729 c.Check(info11.Revision, Equals, snap.R(11)) 10730 c.Check(info11.Version, Equals, "1.1") 10731 10732 instance := snapStates["name1_instance"] 10733 c.Assert(instance, NotNil) 10734 10735 c.Check(instance.Active, Equals, true) 10736 c.Check(instance.CurrentSideInfo(), NotNil) 10737 10738 info13, err := snap.ReadInfo("name1_instance", instance.CurrentSideInfo()) 10739 c.Assert(err, IsNil) 10740 10741 c.Check(info13.InstanceName(), Equals, "name1_instance") 10742 c.Check(info13.SnapName(), Equals, "name1") 10743 c.Check(info13.Revision, Equals, snap.R(13)) 10744 c.Check(info13.Summary(), Equals, "s13 instance") 10745 c.Check(info13.Version, Equals, "1.3") 10746 c.Check(info13.Description(), Equals, "Lots of text") 10747 10748 info13other, err := snap.ReadInfo("name1_instance", instance.Sequence[0]) 10749 c.Assert(err, IsNil) 10750 c.Check(info13, DeepEquals, info13other) 10751 } 10752 10753 func (s *snapmgrQuerySuite) TestAllEmptyAndEmptyNormalisation(c *C) { 10754 st := state.New(nil) 10755 st.Lock() 10756 defer st.Unlock() 10757 10758 snapStates, err := snapstate.All(st) 10759 c.Assert(err, IsNil) 10760 c.Check(snapStates, HasLen, 0) 10761 10762 n, err := snapstate.NumSnaps(st) 10763 c.Assert(err, IsNil) 10764 c.Check(n, Equals, 0) 10765 10766 snapstate.Set(st, "foo", nil) 10767 10768 snapStates, err = snapstate.All(st) 10769 c.Assert(err, IsNil) 10770 c.Check(snapStates, HasLen, 0) 10771 10772 n, err = snapstate.NumSnaps(st) 10773 c.Assert(err, IsNil) 10774 c.Check(n, Equals, 0) 10775 10776 snapstate.Set(st, "foo", &snapstate.SnapState{}) 10777 10778 snapStates, err = snapstate.All(st) 10779 c.Assert(err, IsNil) 10780 c.Check(snapStates, HasLen, 0) 10781 10782 n, err = snapstate.NumSnaps(st) 10783 c.Assert(err, IsNil) 10784 c.Check(n, Equals, 0) 10785 } 10786 10787 func (s *snapmgrTestSuite) TestTrySetsTryMode(c *C) { 10788 s.testTrySetsTryMode(snapstate.Flags{}, c) 10789 } 10790 10791 func (s *snapmgrTestSuite) TestTrySetsTryModeDevMode(c *C) { 10792 s.testTrySetsTryMode(snapstate.Flags{DevMode: true}, c) 10793 } 10794 func (s *snapmgrTestSuite) TestTrySetsTryModeJailMode(c *C) { 10795 s.testTrySetsTryMode(snapstate.Flags{JailMode: true}, c) 10796 } 10797 func (s *snapmgrTestSuite) TestTrySetsTryModeClassic(c *C) { 10798 restore := maybeMockClassicSupport(c) 10799 defer restore() 10800 10801 s.testTrySetsTryMode(snapstate.Flags{Classic: true}, c, "confinement: classic\n") 10802 } 10803 10804 func (s *snapmgrTestSuite) testTrySetsTryMode(flags snapstate.Flags, c *C, extraYaml ...string) { 10805 s.state.Lock() 10806 defer s.state.Unlock() 10807 10808 // make mock try dir 10809 d := c.MkDir() 10810 c.Assert(os.Chmod(d, 0755), IsNil) 10811 tryYaml := filepath.Join(d, "meta", "snap.yaml") 10812 err := os.MkdirAll(filepath.Dir(tryYaml), 0755) 10813 c.Assert(err, IsNil) 10814 buf := bytes.Buffer{} 10815 buf.WriteString("name: foo\nversion: 1.0\n") 10816 if len(extraYaml) > 0 { 10817 for _, extra := range extraYaml { 10818 buf.WriteString(extra) 10819 } 10820 } 10821 err = ioutil.WriteFile(tryYaml, buf.Bytes(), 0644) 10822 c.Assert(err, IsNil) 10823 10824 chg := s.state.NewChange("try", "try snap") 10825 ts, err := snapstate.TryPath(s.state, "foo", d, flags) 10826 c.Assert(err, IsNil) 10827 chg.AddAll(ts) 10828 10829 s.state.Unlock() 10830 defer s.se.Stop() 10831 s.settle(c) 10832 s.state.Lock() 10833 10834 c.Assert(chg.Err(), IsNil) 10835 c.Assert(chg.IsReady(), Equals, true) 10836 10837 // verify snap is in TryMode 10838 var snapst snapstate.SnapState 10839 err = snapstate.Get(s.state, "foo", &snapst) 10840 c.Assert(err, IsNil) 10841 10842 flags.TryMode = true 10843 c.Check(snapst.Flags, DeepEquals, flags) 10844 10845 c.Check(s.state.TaskCount(), Equals, len(ts.Tasks())) 10846 c.Check(taskKinds(ts.Tasks()), DeepEquals, []string{ 10847 "prerequisites", 10848 "prepare-snap", 10849 "mount-snap", 10850 "copy-snap-data", 10851 "setup-profiles", 10852 "link-snap", 10853 "auto-connect", 10854 "set-auto-aliases", 10855 "setup-aliases", 10856 "run-hook[install]", 10857 "start-snap-services", 10858 "run-hook[configure]", 10859 "run-hook[check-health]", 10860 }) 10861 10862 } 10863 10864 func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlag(c *C) { 10865 restore := maybeMockClassicSupport(c) 10866 defer restore() 10867 s.testTrySetsTryMode(snapstate.Flags{}, c) 10868 } 10869 10870 func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlagLeavesDevMode(c *C) { 10871 s.testTrySetsTryMode(snapstate.Flags{DevMode: true}, c) 10872 } 10873 func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlagLeavesJailMode(c *C) { 10874 s.testTrySetsTryMode(snapstate.Flags{JailMode: true}, c) 10875 } 10876 func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlagLeavesClassic(c *C) { 10877 restore := maybeMockClassicSupport(c) 10878 defer restore() 10879 s.testTrySetsTryMode(snapstate.Flags{Classic: true}, c, "confinement: classic\n") 10880 } 10881 10882 func (s *snapmgrTestSuite) testTryUndoRemovesTryFlag(flags snapstate.Flags, c *C) { 10883 s.state.Lock() 10884 defer s.state.Unlock() 10885 10886 // simulate existing state for foo 10887 var snapst snapstate.SnapState 10888 snapst.Sequence = []*snap.SideInfo{ 10889 { 10890 RealName: "foo", 10891 Revision: snap.R(23), 10892 }, 10893 } 10894 snapst.Flags = flags 10895 snapst.Current = snap.R(23) 10896 snapstate.Set(s.state, "foo", &snapst) 10897 c.Check(snapst.TryMode, Equals, false) 10898 10899 chg := s.state.NewChange("try", "try snap") 10900 ts, err := snapstate.TryPath(s.state, "foo", c.MkDir(), flags) 10901 c.Assert(err, IsNil) 10902 chg.AddAll(ts) 10903 10904 last := ts.Tasks()[len(ts.Tasks())-1] 10905 terr := s.state.NewTask("error-trigger", "provoking total undo") 10906 terr.WaitFor(last) 10907 chg.AddTask(terr) 10908 10909 s.state.Unlock() 10910 defer s.se.Stop() 10911 s.settle(c) 10912 s.state.Lock() 10913 10914 // verify snap is not in try mode, the state got undone 10915 err = snapstate.Get(s.state, "foo", &snapst) 10916 c.Assert(err, IsNil) 10917 c.Check(snapst.Flags, DeepEquals, flags) 10918 } 10919 10920 type snapStateSuite struct{} 10921 10922 var _ = Suite(&snapStateSuite{}) 10923 10924 func (s *snapStateSuite) TestSnapStateDevMode(c *C) { 10925 snapst := &snapstate.SnapState{} 10926 c.Check(snapst.DevMode, Equals, false) 10927 snapst.Flags.DevMode = true 10928 c.Check(snapst.DevMode, Equals, true) 10929 } 10930 10931 func (s *snapStateSuite) TestSnapStateType(c *C) { 10932 snapst := &snapstate.SnapState{} 10933 _, err := snapst.Type() 10934 c.Check(err, ErrorMatches, "snap type unset") 10935 10936 snapst.SetType(snap.TypeKernel) 10937 typ, err := snapst.Type() 10938 c.Assert(err, IsNil) 10939 c.Check(typ, Equals, snap.TypeKernel) 10940 } 10941 10942 func (s *snapStateSuite) TestCurrentSideInfoEmpty(c *C) { 10943 var snapst snapstate.SnapState 10944 c.Check(snapst.CurrentSideInfo(), IsNil) 10945 c.Check(snapst.Current.Unset(), Equals, true) 10946 } 10947 10948 func (s *snapStateSuite) TestCurrentSideInfoSimple(c *C) { 10949 si1 := &snap.SideInfo{Revision: snap.R(1)} 10950 snapst := snapstate.SnapState{ 10951 Sequence: []*snap.SideInfo{si1}, 10952 Current: snap.R(1), 10953 } 10954 c.Check(snapst.CurrentSideInfo(), DeepEquals, si1) 10955 } 10956 10957 func (s *snapStateSuite) TestCurrentSideInfoInOrder(c *C) { 10958 si1 := &snap.SideInfo{Revision: snap.R(1)} 10959 si2 := &snap.SideInfo{Revision: snap.R(2)} 10960 snapst := snapstate.SnapState{ 10961 Sequence: []*snap.SideInfo{si1, si2}, 10962 Current: snap.R(2), 10963 } 10964 c.Check(snapst.CurrentSideInfo(), DeepEquals, si2) 10965 } 10966 10967 func (s *snapStateSuite) TestCurrentSideInfoOutOfOrder(c *C) { 10968 si1 := &snap.SideInfo{Revision: snap.R(1)} 10969 si2 := &snap.SideInfo{Revision: snap.R(2)} 10970 snapst := snapstate.SnapState{ 10971 Sequence: []*snap.SideInfo{si1, si2}, 10972 Current: snap.R(1), 10973 } 10974 c.Check(snapst.CurrentSideInfo(), DeepEquals, si1) 10975 } 10976 10977 func (s *snapStateSuite) TestCurrentSideInfoInconsistent(c *C) { 10978 snapst := snapstate.SnapState{ 10979 Sequence: []*snap.SideInfo{ 10980 {Revision: snap.R(1)}, 10981 }, 10982 } 10983 c.Check(func() { snapst.CurrentSideInfo() }, PanicMatches, `snapst.Current and snapst.Sequence out of sync:.*`) 10984 } 10985 10986 func (s *snapStateSuite) TestCurrentSideInfoInconsistentWithCurrent(c *C) { 10987 snapst := snapstate.SnapState{Current: snap.R(17)} 10988 c.Check(func() { snapst.CurrentSideInfo() }, PanicMatches, `cannot find snapst.Current in the snapst.Sequence`) 10989 } 10990 10991 func (snapStateSuite) TestDefaultContentPlugProviders(c *C) { 10992 info := &snap.Info{ 10993 Plugs: map[string]*snap.PlugInfo{}, 10994 } 10995 10996 info.Plugs["foo"] = &snap.PlugInfo{ 10997 Snap: info, 10998 Name: "sound-themes", 10999 Interface: "content", 11000 Attrs: map[string]interface{}{"default-provider": "common-themes", "content": "foo"}, 11001 } 11002 info.Plugs["bar"] = &snap.PlugInfo{ 11003 Snap: info, 11004 Name: "visual-themes", 11005 Interface: "content", 11006 Attrs: map[string]interface{}{"default-provider": "common-themes", "content": "bar"}, 11007 } 11008 info.Plugs["baz"] = &snap.PlugInfo{ 11009 Snap: info, 11010 Name: "not-themes", 11011 Interface: "content", 11012 Attrs: map[string]interface{}{"default-provider": "some-snap", "content": "baz"}, 11013 } 11014 info.Plugs["qux"] = &snap.PlugInfo{Snap: info, Interface: "not-content"} 11015 11016 st := state.New(nil) 11017 st.Lock() 11018 defer st.Unlock() 11019 11020 repo := interfaces.NewRepository() 11021 ifacerepo.Replace(st, repo) 11022 11023 providers := snapstate.DefaultContentPlugProviders(st, info) 11024 sort.Strings(providers) 11025 c.Check(providers, DeepEquals, []string{"common-themes", "some-snap"}) 11026 } 11027 11028 type snapSetupSuite struct{} 11029 11030 var _ = Suite(&snapSetupSuite{}) 11031 11032 type canRemoveSuite struct { 11033 st *state.State 11034 deviceCtx snapstate.DeviceContext 11035 11036 bootloader *bootloadertest.MockBootloader 11037 } 11038 11039 var _ = Suite(&canRemoveSuite{}) 11040 11041 func (s *canRemoveSuite) SetUpTest(c *C) { 11042 dirs.SetRootDir(c.MkDir()) 11043 s.st = state.New(nil) 11044 s.deviceCtx = &snapstatetest.TrivialDeviceContext{DeviceModel: DefaultModel()} 11045 11046 s.bootloader = bootloadertest.Mock("mock", c.MkDir()) 11047 bootloader.Force(s.bootloader) 11048 } 11049 11050 func (s *canRemoveSuite) TearDownTest(c *C) { 11051 dirs.SetRootDir("/") 11052 bootloader.Force(nil) 11053 } 11054 11055 func (s *canRemoveSuite) TestAppAreAlwaysOKToRemove(c *C) { 11056 info := &snap.Info{ 11057 SnapType: snap.TypeApp, 11058 } 11059 info.RealName = "foo" 11060 11061 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, false, s.deviceCtx), Equals, true) 11062 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, true, s.deviceCtx), Equals, true) 11063 } 11064 11065 func (s *canRemoveSuite) TestLastGadgetsAreNotOK(c *C) { 11066 info := &snap.Info{ 11067 SnapType: snap.TypeGadget, 11068 } 11069 info.RealName = "foo" 11070 11071 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false) 11072 } 11073 11074 func (s *canRemoveSuite) TestLastOSAndKernelAreNotOK(c *C) { 11075 os := &snap.Info{ 11076 SnapType: snap.TypeOS, 11077 } 11078 os.RealName = "os" 11079 kernel := &snap.Info{ 11080 SnapType: snap.TypeKernel, 11081 } 11082 // this kernel part of the model 11083 kernel.RealName = "kernel" 11084 11085 c.Check(snapstate.CanRemove(s.st, os, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false) 11086 11087 c.Check(snapstate.CanRemove(s.st, kernel, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false) 11088 } 11089 11090 func (s *canRemoveSuite) TestKernelBootInUseIsKept(c *C) { 11091 kernel := &snap.Info{ 11092 SnapType: snap.TypeKernel, 11093 SideInfo: snap.SideInfo{ 11094 Revision: snap.R(3), 11095 }, 11096 } 11097 kernel.RealName = "kernel" 11098 11099 s.bootloader.SetBootKernel(fmt.Sprintf("%s_%s.snap", kernel.RealName, kernel.SideInfo.Revision)) 11100 11101 removeAll := false 11102 c.Check(snapstate.CanRemove(s.st, kernel, &snapstate.SnapState{}, removeAll, s.deviceCtx), Equals, false) 11103 } 11104 11105 func (s *canRemoveSuite) TestOstInUseIsKept(c *C) { 11106 base := &snap.Info{ 11107 SnapType: snap.TypeBase, 11108 SideInfo: snap.SideInfo{ 11109 Revision: snap.R(3), 11110 }, 11111 } 11112 base.RealName = "core18" 11113 11114 s.bootloader.SetBootBase(fmt.Sprintf("%s_%s.snap", base.RealName, base.SideInfo.Revision)) 11115 11116 removeAll := false 11117 c.Check(snapstate.CanRemove(s.st, base, &snapstate.SnapState{}, removeAll, s.deviceCtx), Equals, false) 11118 } 11119 11120 func (s *canRemoveSuite) TestRemoveNonModelKernelIsOk(c *C) { 11121 kernel := &snap.Info{ 11122 SnapType: snap.TypeKernel, 11123 } 11124 kernel.RealName = "other-non-model-kernel" 11125 11126 c.Check(snapstate.CanRemove(s.st, kernel, &snapstate.SnapState{}, true, s.deviceCtx), Equals, true) 11127 } 11128 11129 func (s *canRemoveSuite) TestRemoveNonModelKernelStillInUseNotOk(c *C) { 11130 kernel := &snap.Info{ 11131 SnapType: snap.TypeKernel, 11132 SideInfo: snap.SideInfo{ 11133 Revision: snap.R(2), 11134 }, 11135 } 11136 kernel.RealName = "other-non-model-kernel" 11137 11138 s.bootloader.SetBootKernel(fmt.Sprintf("%s_%s.snap", kernel.RealName, kernel.SideInfo.Revision)) 11139 11140 c.Check(snapstate.CanRemove(s.st, kernel, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false) 11141 } 11142 11143 func (s *canRemoveSuite) TestLastOSWithModelBaseIsOk(c *C) { 11144 s.st.Lock() 11145 defer s.st.Unlock() 11146 11147 deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: ModelWithBase("core18")} 11148 os := &snap.Info{ 11149 SnapType: snap.TypeOS, 11150 } 11151 os.RealName = "os" 11152 11153 c.Check(snapstate.CanRemove(s.st, os, &snapstate.SnapState{}, true, deviceCtx), Equals, true) 11154 } 11155 11156 func (s *canRemoveSuite) TestLastOSWithModelBaseButOsInUse(c *C) { 11157 s.st.Lock() 11158 defer s.st.Unlock() 11159 11160 deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: ModelWithBase("core18")} 11161 11162 // pretend we have a snap installed that has no base (which means 11163 // it needs core) 11164 si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)} 11165 snaptest.MockSnap(c, "name: some-snap\nversion: 1.0", si) 11166 snapstate.Set(s.st, "some-snap", &snapstate.SnapState{ 11167 Active: true, 11168 Sequence: []*snap.SideInfo{si}, 11169 Current: snap.R(1), 11170 }) 11171 11172 // now pretend we want to remove the core snap 11173 os := &snap.Info{ 11174 SnapType: snap.TypeOS, 11175 } 11176 os.RealName = "core" 11177 c.Check(snapstate.CanRemove(s.st, os, &snapstate.SnapState{}, true, deviceCtx), Equals, false) 11178 } 11179 11180 func (s *canRemoveSuite) TestOneRevisionIsOK(c *C) { 11181 info := &snap.Info{ 11182 SnapType: snap.TypeGadget, 11183 } 11184 info.RealName = "foo" 11185 11186 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, false, s.deviceCtx), Equals, true) 11187 } 11188 11189 func (s *canRemoveSuite) TestRequiredIsNotOK(c *C) { 11190 info := &snap.Info{ 11191 SnapType: snap.TypeApp, 11192 } 11193 info.RealName = "foo" 11194 11195 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: false, Flags: snapstate.Flags{Required: true}}, true, s.deviceCtx), Equals, false) 11196 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true, Flags: snapstate.Flags{Required: true}}, true, s.deviceCtx), Equals, false) 11197 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true, Flags: snapstate.Flags{Required: true}}, false, s.deviceCtx), Equals, true) 11198 } 11199 11200 func (s *canRemoveSuite) TestBaseUnused(c *C) { 11201 s.st.Lock() 11202 defer s.st.Unlock() 11203 11204 info := &snap.Info{ 11205 SnapType: snap.TypeBase, 11206 } 11207 info.RealName = "some-base" 11208 11209 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, false, s.deviceCtx), Equals, true) 11210 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, true, s.deviceCtx), Equals, true) 11211 } 11212 11213 func (s *canRemoveSuite) TestBaseInUse(c *C) { 11214 s.st.Lock() 11215 defer s.st.Unlock() 11216 11217 // pretend we have a snap installed that uses "some-base" 11218 si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)} 11219 snaptest.MockSnap(c, "name: some-snap\nversion: 1.0\nbase: some-base", si) 11220 snapstate.Set(s.st, "some-snap", &snapstate.SnapState{ 11221 Active: true, 11222 Sequence: []*snap.SideInfo{si}, 11223 Current: snap.R(1), 11224 }) 11225 11226 // pretend now we want to remove "some-base" 11227 info := &snap.Info{ 11228 SnapType: snap.TypeBase, 11229 } 11230 info.RealName = "some-base" 11231 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, true, s.deviceCtx), Equals, false) 11232 } 11233 11234 func (s *canRemoveSuite) TestBaseInUseOtherRevision(c *C) { 11235 s.st.Lock() 11236 defer s.st.Unlock() 11237 11238 // pretend we have a snap installed that uses "some-base" 11239 si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)} 11240 si2 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)} 11241 // older revision uses base 11242 snaptest.MockSnap(c, "name: some-snap\nversion: 1.0\nbase: some-base", si) 11243 // new one does not 11244 snaptest.MockSnap(c, "name: some-snap\nversion: 1.0\n", si2) 11245 snapstate.Set(s.st, "some-snap", &snapstate.SnapState{ 11246 Active: true, 11247 Sequence: []*snap.SideInfo{si, si2}, 11248 Current: snap.R(2), 11249 }) 11250 11251 // pretend now we want to remove "some-base" 11252 info := &snap.Info{ 11253 SnapType: snap.TypeBase, 11254 } 11255 info.RealName = "some-base" 11256 // revision 1 requires some-base 11257 c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, true, s.deviceCtx), Equals, false) 11258 11259 // now pretend we want to remove the core snap 11260 os := &snap.Info{ 11261 SnapType: snap.TypeOS, 11262 } 11263 os.RealName = "core" 11264 // but revision 2 requires core 11265 c.Check(snapstate.CanRemove(s.st, os, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false) 11266 } 11267 11268 func revs(seq []*snap.SideInfo) []int { 11269 revs := make([]int, len(seq)) 11270 for i, si := range seq { 11271 revs[i] = si.Revision.N 11272 } 11273 11274 return revs 11275 } 11276 11277 type opSeqOpts struct { 11278 revert bool 11279 fail bool 11280 before []int 11281 current int 11282 via int 11283 after []int 11284 } 11285 11286 // build a SnapState with a revision sequence given by `before` and a 11287 // current revision of `current`. Then refresh --revision via. Then 11288 // check the revision sequence is as in `after`. 11289 func (s *snapmgrTestSuite) testOpSequence(c *C, opts *opSeqOpts) (*snapstate.SnapState, *state.TaskSet) { 11290 s.state.Lock() 11291 defer s.state.Unlock() 11292 11293 seq := make([]*snap.SideInfo, len(opts.before)) 11294 for i, n := range opts.before { 11295 seq[i] = &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(n)} 11296 } 11297 11298 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 11299 Active: true, 11300 Channel: "edge", 11301 Sequence: seq, 11302 Current: snap.R(opts.current), 11303 SnapType: "app", 11304 }) 11305 11306 var chg *state.Change 11307 var ts *state.TaskSet 11308 var err error 11309 if opts.revert { 11310 chg = s.state.NewChange("revert", "revert a snap") 11311 ts, err = snapstate.RevertToRevision(s.state, "some-snap", snap.R(opts.via), snapstate.Flags{}) 11312 } else { 11313 chg = s.state.NewChange("refresh", "refresh a snap") 11314 ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(opts.via)}, s.user.ID, snapstate.Flags{}) 11315 } 11316 c.Assert(err, IsNil) 11317 if opts.fail { 11318 tasks := ts.Tasks() 11319 var last *state.Task 11320 // don't make a task wait on rerefresh, that's bad 11321 for i := len(tasks) - 1; i > 0; i-- { 11322 last = tasks[i] 11323 if last.Kind() != "check-rerefresh" { 11324 break 11325 } 11326 } 11327 terr := s.state.NewTask("error-trigger", "provoking total undo") 11328 terr.WaitFor(last) 11329 if len(last.Lanes()) > 0 { 11330 lanes := last.Lanes() 11331 // sanity 11332 c.Assert(lanes, HasLen, 1) 11333 terr.JoinLane(lanes[0]) 11334 } 11335 chg.AddTask(terr) 11336 } 11337 chg.AddAll(ts) 11338 11339 s.state.Unlock() 11340 defer s.se.Stop() 11341 s.settle(c) 11342 s.state.Lock() 11343 11344 var snapst snapstate.SnapState 11345 err = snapstate.Get(s.state, "some-snap", &snapst) 11346 c.Assert(err, IsNil) 11347 c.Check(revs(snapst.Sequence), DeepEquals, opts.after) 11348 11349 return &snapst, ts 11350 } 11351 11352 func (s *snapmgrTestSuite) testUpdateSequence(c *C, opts *opSeqOpts) *state.TaskSet { 11353 restore := release.MockOnClassic(false) 11354 defer restore() 11355 11356 opts.revert = false 11357 snapst, ts := s.testOpSequence(c, opts) 11358 // update always ends with current==seq[-1]==via: 11359 c.Check(snapst.Current.N, Equals, opts.after[len(opts.after)-1]) 11360 c.Check(snapst.Current.N, Equals, opts.via) 11361 11362 c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 1) 11363 c.Check(s.fakeBackend.ops.First("copy-data"), DeepEquals, &fakeOp{ 11364 op: "copy-data", 11365 path: fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via), 11366 old: fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.current), 11367 }) 11368 11369 return ts 11370 } 11371 11372 func (s *snapmgrTestSuite) testUpdateFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet { 11373 restore := release.MockOnClassic(false) 11374 defer restore() 11375 11376 opts.revert = false 11377 opts.after = opts.before 11378 s.fakeBackend.linkSnapFailTrigger = fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via) 11379 snapst, ts := s.testOpSequence(c, opts) 11380 // a failed update will always end with current unchanged 11381 c.Check(snapst.Current.N, Equals, opts.current) 11382 11383 ops := s.fakeBackend.ops 11384 c.Check(ops.Count("copy-data"), Equals, 1) 11385 do := ops.First("copy-data") 11386 11387 c.Check(ops.Count("undo-copy-snap-data"), Equals, 1) 11388 undo := ops.First("undo-copy-snap-data") 11389 11390 do.op = undo.op 11391 c.Check(do, DeepEquals, undo) // i.e. they only differed in the op 11392 11393 return ts 11394 } 11395 11396 // testTotal*Failure fails *after* link-snap 11397 func (s *snapmgrTestSuite) testTotalUpdateFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet { 11398 restore := release.MockOnClassic(false) 11399 defer restore() 11400 11401 opts.revert = false 11402 opts.fail = true 11403 snapst, ts := s.testOpSequence(c, opts) 11404 // a failed update will always end with current unchanged 11405 c.Check(snapst.Current.N, Equals, opts.current) 11406 11407 ops := s.fakeBackend.ops 11408 c.Check(ops.Count("copy-data"), Equals, 1) 11409 do := ops.First("copy-data") 11410 11411 c.Check(ops.Count("undo-copy-snap-data"), Equals, 1) 11412 undo := ops.First("undo-copy-snap-data") 11413 11414 do.op = undo.op 11415 c.Check(do, DeepEquals, undo) // i.e. they only differed in the op 11416 11417 return ts 11418 } 11419 11420 func (s *snapmgrTestSuite) testRevertSequence(c *C, opts *opSeqOpts) *state.TaskSet { 11421 opts.revert = true 11422 opts.after = opts.before 11423 snapst, ts := s.testOpSequence(c, opts) 11424 // successful revert leaves current == via 11425 c.Check(snapst.Current.N, Equals, opts.via) 11426 11427 c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0) 11428 11429 return ts 11430 } 11431 11432 func (s *snapmgrTestSuite) testRevertFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet { 11433 opts.revert = true 11434 opts.after = opts.before 11435 s.fakeBackend.linkSnapFailTrigger = fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via) 11436 snapst, ts := s.testOpSequence(c, opts) 11437 // a failed revert will always end with current unchanged 11438 c.Check(snapst.Current.N, Equals, opts.current) 11439 11440 c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0) 11441 c.Check(s.fakeBackend.ops.Count("undo-copy-snap-data"), Equals, 0) 11442 11443 return ts 11444 } 11445 11446 func (s *snapmgrTestSuite) testTotalRevertFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet { 11447 opts.revert = true 11448 opts.fail = true 11449 opts.after = opts.before 11450 snapst, ts := s.testOpSequence(c, opts) 11451 // a failed revert will always end with current unchanged 11452 c.Check(snapst.Current.N, Equals, opts.current) 11453 11454 c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0) 11455 c.Check(s.fakeBackend.ops.Count("undo-copy-snap-data"), Equals, 0) 11456 11457 return ts 11458 } 11459 11460 // *** sequence tests *** 11461 11462 // 1. a boring update 11463 // 1a. ... that works 11464 func (s *snapmgrTestSuite) TestSeqNormal(c *C) { 11465 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4, after: []int{2, 3, 4}}) 11466 } 11467 11468 // 1b. that fails during link 11469 func (s *snapmgrTestSuite) TestSeqNormalFailure(c *C) { 11470 s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4}) 11471 } 11472 11473 // 1c. that fails after link 11474 func (s *snapmgrTestSuite) TestSeqTotalNormalFailure(c *C) { 11475 // total updates are failures after sequence trimming => we lose a rev 11476 s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4, after: []int{2, 3}}) 11477 } 11478 11479 // 2. a boring revert 11480 // 2a. that works 11481 func (s *snapmgrTestSuite) TestSeqRevert(c *C) { 11482 s.testRevertSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2}) 11483 } 11484 11485 // 2b. that fails during link 11486 func (s *snapmgrTestSuite) TestSeqRevertFailure(c *C) { 11487 s.testRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2}) 11488 } 11489 11490 // 2c. that fails after link 11491 func (s *snapmgrTestSuite) TestSeqTotalRevertFailure(c *C) { 11492 s.testTotalRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2}) 11493 } 11494 11495 // 3. a post-revert update 11496 // 3a. that works 11497 func (s *snapmgrTestSuite) TestSeqPostRevert(c *C) { 11498 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4, after: []int{1, 2, 4}}) 11499 } 11500 11501 // 3b. that fails during link 11502 func (s *snapmgrTestSuite) TestSeqPostRevertFailure(c *C) { 11503 s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4}) 11504 } 11505 11506 // 3c. that fails after link 11507 func (s *snapmgrTestSuite) TestSeqTotalPostRevertFailure(c *C) { 11508 // lose a rev here as well 11509 s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4, after: []int{1, 2}}) 11510 } 11511 11512 // 3d. manually requesting the one reverted away from 11513 func (s *snapmgrTestSuite) TestSeqRefreshPostRevertSameRevno(c *C) { 11514 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 3, after: []int{1, 2, 3}}) 11515 } 11516 11517 // 4. a post-revert revert 11518 // 4a. that works 11519 func (s *snapmgrTestSuite) TestSeqRevertPostRevert(c *C) { 11520 s.testRevertSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1}) 11521 } 11522 11523 // 4b. that fails during link 11524 func (s *snapmgrTestSuite) TestSeqRevertPostRevertFailure(c *C) { 11525 s.testRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1}) 11526 } 11527 11528 // 4c. that fails after link 11529 func (s *snapmgrTestSuite) TestSeqTotalRevertPostRevertFailure(c *C) { 11530 s.testTotalRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1}) 11531 } 11532 11533 // 5. an update that missed a rev 11534 // 5a. that works 11535 func (s *snapmgrTestSuite) TestSeqMissedOne(c *C) { 11536 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4, after: []int{1, 2, 4}}) 11537 } 11538 11539 // 5b. that fails during link 11540 func (s *snapmgrTestSuite) TestSeqMissedOneFailure(c *C) { 11541 s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4}) 11542 } 11543 11544 // 5c. that fails after link 11545 func (s *snapmgrTestSuite) TestSeqTotalMissedOneFailure(c *C) { 11546 // we don't lose a rev here because len(Seq) < 3 going in 11547 s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4, after: []int{1, 2}}) 11548 } 11549 11550 // 6. an update that updates to a revision we already have ("ABA update") 11551 // 6a. that works 11552 func (s *snapmgrTestSuite) TestSeqABA(c *C) { 11553 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2, after: []int{1, 3, 2}}) 11554 c.Check(s.fakeBackend.ops[len(s.fakeBackend.ops)-1], DeepEquals, fakeOp{ 11555 op: "cleanup-trash", 11556 name: "some-snap", 11557 revno: snap.R(2), 11558 }) 11559 } 11560 11561 // 6b. that fails during link 11562 func (s *snapmgrTestSuite) TestSeqABAFailure(c *C) { 11563 s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2}) 11564 c.Check(s.fakeBackend.ops.First("cleanup-trash"), IsNil) 11565 } 11566 11567 // 6c that fails after link 11568 func (s *snapmgrTestSuite) TestSeqTotalABAFailure(c *C) { 11569 // we don't lose a rev here because ABA 11570 s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2, after: []int{1, 2, 3}}) 11571 // XXX: TODO: NOTE!! WARNING!! etc 11572 // 11573 // if this happens in real life, things will be weird. revno 2 will 11574 // have data that has been copied from 3, instead of old 2's data, 11575 // because the failure occurred *after* nuking the trash. This can 11576 // happen when things are chained. Because of this, if it were to 11577 // *actually* happen the correct end sequence would be [1, 3] and not 11578 // [1, 2, 3]. IRL this scenario can happen if an update that works is 11579 // chained to an update that fails. Detecting this case is rather hard, 11580 // and the end result is not nice, and we want to move cleanup to a 11581 // separate handler & status that will cope with this better (so trash 11582 // gets nuked after all tasks succeeded). 11583 } 11584 11585 func (s *snapmgrTestSuite) TestSeqRetainConf(c *C) { 11586 revseq := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 11587 11588 for i := 2; i <= 10; i++ { 11589 // wot, me, hacky? 11590 s.TearDownTest(c) 11591 s.SetUpTest(c) 11592 s.state.Lock() 11593 tr := config.NewTransaction(s.state) 11594 tr.Set("core", "refresh.retain", i) 11595 tr.Commit() 11596 s.state.Unlock() 11597 11598 s.testUpdateSequence(c, &opSeqOpts{before: revseq[:9], current: 9, via: 10, after: revseq[10-i:]}) 11599 } 11600 } 11601 11602 func (s *snapmgrTestSuite) TestUpdateTasksWithOldCurrent(c *C) { 11603 s.state.Lock() 11604 defer s.state.Unlock() 11605 restore := release.MockOnClassic(false) 11606 defer restore() 11607 11608 si1 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)} 11609 si2 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)} 11610 si3 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)} 11611 si4 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)} 11612 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 11613 Active: true, 11614 Channel: "edge", 11615 Sequence: []*snap.SideInfo{si1, si2, si3, si4}, 11616 Current: snap.R(2), 11617 SnapType: "app", 11618 }) 11619 11620 // run the update 11621 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 11622 c.Assert(err, IsNil) 11623 11624 verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 2, ts, s.state) 11625 11626 // and ensure that it will remove the revisions after "current" 11627 // (si3, si4) 11628 var snapsup snapstate.SnapSetup 11629 tasks := ts.Tasks() 11630 11631 i := len(tasks) - 8 11632 c.Check(tasks[i].Kind(), Equals, "clear-snap") 11633 err = tasks[i].Get("snap-setup", &snapsup) 11634 c.Assert(err, IsNil) 11635 c.Check(snapsup.Revision(), Equals, si3.Revision) 11636 11637 i = len(tasks) - 6 11638 c.Check(tasks[i].Kind(), Equals, "clear-snap") 11639 err = tasks[i].Get("snap-setup", &snapsup) 11640 c.Assert(err, IsNil) 11641 c.Check(snapsup.Revision(), Equals, si4.Revision) 11642 } 11643 11644 func (s *snapmgrTestSuite) TestUpdateCanDoBackwards(c *C) { 11645 si7 := snap.SideInfo{ 11646 RealName: "some-snap", 11647 SnapID: "some-snap-id", 11648 Revision: snap.R(7), 11649 } 11650 si11 := snap.SideInfo{ 11651 RealName: "some-snap", 11652 SnapID: "some-snap-id", 11653 Revision: snap.R(11), 11654 } 11655 11656 s.state.Lock() 11657 defer s.state.Unlock() 11658 11659 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 11660 Active: true, 11661 Sequence: []*snap.SideInfo{&si7, &si11}, 11662 Current: si11.Revision, 11663 SnapType: "app", 11664 }) 11665 11666 chg := s.state.NewChange("refresh", "refresh a snap") 11667 ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(7)}, s.user.ID, snapstate.Flags{}) 11668 c.Assert(err, IsNil) 11669 chg.AddAll(ts) 11670 11671 s.state.Unlock() 11672 defer s.se.Stop() 11673 s.settle(c) 11674 s.state.Lock() 11675 expected := fakeOps{ 11676 { 11677 op: "remove-snap-aliases", 11678 name: "some-snap", 11679 }, 11680 { 11681 op: "unlink-snap", 11682 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 11683 }, 11684 { 11685 op: "copy-data", 11686 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 11687 old: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 11688 }, 11689 { 11690 op: "setup-profiles:Doing", 11691 name: "some-snap", 11692 revno: snap.R(7), 11693 }, 11694 { 11695 op: "candidate", 11696 sinfo: snap.SideInfo{ 11697 RealName: "some-snap", 11698 SnapID: "some-snap-id", 11699 Channel: "", 11700 Revision: snap.R(7), 11701 }, 11702 }, 11703 { 11704 op: "link-snap", 11705 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 11706 }, 11707 { 11708 op: "auto-connect:Doing", 11709 name: "some-snap", 11710 revno: snap.R(7), 11711 }, 11712 { 11713 op: "update-aliases", 11714 }, 11715 { 11716 op: "cleanup-trash", 11717 name: "some-snap", 11718 revno: snap.R(7), 11719 }, 11720 } 11721 // start with an easier-to-read error if this fails: 11722 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 11723 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 11724 } 11725 11726 func (s *snapmgrTestSuite) TestSnapStateNoLocalRevision(c *C) { 11727 si7 := snap.SideInfo{ 11728 RealName: "some-snap", 11729 Revision: snap.R(-7), 11730 } 11731 si11 := snap.SideInfo{ 11732 RealName: "some-snap", 11733 Revision: snap.R(-11), 11734 } 11735 snapst := &snapstate.SnapState{ 11736 Sequence: []*snap.SideInfo{&si7, &si11}, 11737 Current: si7.Revision, 11738 } 11739 c.Assert(snapst.LocalRevision(), Equals, snap.R(-11)) 11740 } 11741 11742 func (s *snapmgrTestSuite) TestSnapStateLocalRevision(c *C) { 11743 si7 := snap.SideInfo{ 11744 RealName: "some-snap", 11745 Revision: snap.R(7), 11746 } 11747 snapst := &snapstate.SnapState{ 11748 Sequence: []*snap.SideInfo{&si7}, 11749 Current: si7.Revision, 11750 } 11751 c.Assert(snapst.LocalRevision().Unset(), Equals, true) 11752 } 11753 11754 func (s *snapmgrTestSuite) TestInstallMany(c *C) { 11755 s.state.Lock() 11756 defer s.state.Unlock() 11757 11758 installed, tts, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0) 11759 c.Assert(err, IsNil) 11760 c.Assert(tts, HasLen, 2) 11761 c.Check(installed, DeepEquals, []string{"one", "two"}) 11762 11763 c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true) 11764 11765 for i, ts := range tts { 11766 verifyInstallTasks(c, 0, 0, ts, s.state) 11767 // check that tasksets are in separate lanes 11768 for _, t := range ts.Tasks() { 11769 c.Assert(t.Lanes(), DeepEquals, []int{i + 1}) 11770 } 11771 } 11772 } 11773 11774 func (s *snapmgrTestSuite) TestInstallManyTooEarly(c *C) { 11775 s.state.Lock() 11776 defer s.state.Unlock() 11777 11778 s.state.Set("seeded", nil) 11779 11780 _, _, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0) 11781 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 11782 c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`) 11783 } 11784 11785 func (s *snapmgrTestSuite) TestInstallManyChecksPreconditions(c *C) { 11786 s.state.Lock() 11787 defer s.state.Unlock() 11788 11789 _, _, err := snapstate.InstallMany(s.state, []string{"some-snap-now-classic"}, 0) 11790 c.Assert(err, NotNil) 11791 c.Check(err, DeepEquals, &snapstate.SnapNeedsClassicError{Snap: "some-snap-now-classic"}) 11792 11793 _, _, err = snapstate.InstallMany(s.state, []string{"some-snap_foo"}, 0) 11794 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.parallel-instances' to true") 11795 } 11796 11797 func verifyStopReason(c *C, ts *state.TaskSet, reason string) { 11798 tl := tasksWithKind(ts, "stop-snap-services") 11799 c.Check(tl, HasLen, 1) 11800 11801 var stopReason string 11802 err := tl[0].Get("stop-reason", &stopReason) 11803 c.Assert(err, IsNil) 11804 c.Check(stopReason, Equals, reason) 11805 11806 } 11807 11808 func (s *snapmgrTestSuite) TestRemoveMany(c *C) { 11809 s.state.Lock() 11810 defer s.state.Unlock() 11811 11812 snapstate.Set(s.state, "one", &snapstate.SnapState{ 11813 Active: true, 11814 Sequence: []*snap.SideInfo{ 11815 {RealName: "one", SnapID: "one-id", Revision: snap.R(1)}, 11816 }, 11817 Current: snap.R(1), 11818 }) 11819 snapstate.Set(s.state, "two", &snapstate.SnapState{ 11820 Active: true, 11821 Sequence: []*snap.SideInfo{ 11822 {RealName: "two", SnapID: "two-id", Revision: snap.R(1)}, 11823 }, 11824 Current: snap.R(1), 11825 }) 11826 11827 removed, tts, err := snapstate.RemoveMany(s.state, []string{"one", "two"}) 11828 c.Assert(err, IsNil) 11829 c.Assert(tts, HasLen, 2) 11830 c.Check(removed, DeepEquals, []string{"one", "two"}) 11831 11832 c.Assert(s.state.TaskCount(), Equals, 8*2) 11833 for i, ts := range tts { 11834 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 11835 "stop-snap-services", 11836 "run-hook[remove]", 11837 "auto-disconnect", 11838 "remove-aliases", 11839 "unlink-snap", 11840 "remove-profiles", 11841 "clear-snap", 11842 "discard-snap", 11843 }) 11844 verifyStopReason(c, ts, "remove") 11845 // check that tasksets are in separate lanes 11846 for _, t := range ts.Tasks() { 11847 c.Assert(t.Lanes(), DeepEquals, []int{i + 1}) 11848 } 11849 11850 } 11851 } 11852 11853 func tasksWithKind(ts *state.TaskSet, kind string) []*state.Task { 11854 var tasks []*state.Task 11855 for _, task := range ts.Tasks() { 11856 if task.Kind() == kind { 11857 tasks = append(tasks, task) 11858 } 11859 } 11860 return tasks 11861 } 11862 11863 var gadgetYaml = ` 11864 defaults: 11865 some-snap-ididididididididididid: 11866 key: value 11867 11868 volumes: 11869 volume-id: 11870 bootloader: grub 11871 ` 11872 11873 func (s *snapmgrTestSuite) prepareGadget(c *C, extraGadgetYaml ...string) { 11874 gadgetSideInfo := &snap.SideInfo{RealName: "the-gadget", SnapID: "the-gadget-id", Revision: snap.R(1)} 11875 gadgetInfo := snaptest.MockSnap(c, ` 11876 name: the-gadget 11877 type: gadget 11878 version: 1.0 11879 `, gadgetSideInfo) 11880 11881 gadgetYamlWhole := strings.Join(append([]string{gadgetYaml}, extraGadgetYaml...), "") 11882 err := ioutil.WriteFile(filepath.Join(gadgetInfo.MountDir(), "meta/gadget.yaml"), []byte(gadgetYamlWhole), 0600) 11883 c.Assert(err, IsNil) 11884 11885 snapstate.Set(s.state, "the-gadget", &snapstate.SnapState{ 11886 Active: true, 11887 Sequence: []*snap.SideInfo{&gadgetInfo.SideInfo}, 11888 Current: snap.R(1), 11889 SnapType: "gadget", 11890 }) 11891 } 11892 11893 func deviceWithGadgetContext(gadgetName string) snapstate.DeviceContext { 11894 return &snapstatetest.TrivialDeviceContext{ 11895 DeviceModel: MakeModel(map[string]interface{}{ 11896 "gadget": gadgetName, 11897 }), 11898 } 11899 } 11900 11901 func deviceWithoutGadgetContext() snapstate.DeviceContext { 11902 return &snapstatetest.TrivialDeviceContext{ 11903 DeviceModel: ClassicModel(), 11904 } 11905 } 11906 11907 func (s *snapmgrTestSuite) TestConfigDefaults(c *C) { 11908 r := release.MockOnClassic(false) 11909 defer r() 11910 11911 // using MockSnap, we want to read the bits on disk 11912 snapstate.MockSnapReadInfo(snap.ReadInfo) 11913 11914 s.state.Lock() 11915 defer s.state.Unlock() 11916 11917 s.prepareGadget(c) 11918 11919 deviceCtx := deviceWithGadgetContext("the-gadget") 11920 11921 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 11922 Active: true, 11923 Sequence: []*snap.SideInfo{ 11924 {RealName: "some-snap", Revision: snap.R(11), SnapID: "some-snap-ididididididididididid"}, 11925 }, 11926 Current: snap.R(11), 11927 SnapType: "app", 11928 }) 11929 makeInstalledMockCoreSnap(c) 11930 11931 defls, err := snapstate.ConfigDefaults(s.state, deviceCtx, "some-snap") 11932 c.Assert(err, IsNil) 11933 c.Assert(defls, DeepEquals, map[string]interface{}{"key": "value"}) 11934 11935 snapstate.Set(s.state, "local-snap", &snapstate.SnapState{ 11936 Active: true, 11937 Sequence: []*snap.SideInfo{ 11938 {RealName: "local-snap", Revision: snap.R(5)}, 11939 }, 11940 Current: snap.R(5), 11941 SnapType: "app", 11942 }) 11943 _, err = snapstate.ConfigDefaults(s.state, deviceCtx, "local-snap") 11944 c.Assert(err, Equals, state.ErrNoState) 11945 } 11946 11947 func (s *snapmgrTestSuite) TestConfigDefaultsNoGadget(c *C) { 11948 r := release.MockOnClassic(false) 11949 defer r() 11950 11951 // using MockSnap, we want to read the bits on disk 11952 snapstate.MockSnapReadInfo(snap.ReadInfo) 11953 11954 s.state.Lock() 11955 defer s.state.Unlock() 11956 11957 deviceCtxNoGadget := deviceWithoutGadgetContext() 11958 11959 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 11960 Active: true, 11961 Sequence: []*snap.SideInfo{ 11962 {RealName: "some-snap", Revision: snap.R(11), SnapID: "some-snap-ididididididididididid"}, 11963 }, 11964 Current: snap.R(11), 11965 SnapType: "app", 11966 }) 11967 makeInstalledMockCoreSnap(c) 11968 11969 _, err := snapstate.ConfigDefaults(s.state, deviceCtxNoGadget, "some-snap") 11970 c.Assert(err, Equals, state.ErrNoState) 11971 } 11972 11973 func (s *snapmgrTestSuite) TestConfigDefaultsSystemWithCore(c *C) { 11974 r := release.MockOnClassic(false) 11975 defer r() 11976 11977 // using MockSnapReadInfo, we want to read the bits on disk 11978 snapstate.MockSnapReadInfo(snap.ReadInfo) 11979 11980 s.state.Lock() 11981 defer s.state.Unlock() 11982 11983 s.prepareGadget(c, ` 11984 defaults: 11985 system: 11986 foo: bar 11987 `) 11988 11989 deviceCtx := deviceWithGadgetContext("the-gadget") 11990 11991 snapstate.Set(s.state, "core", &snapstate.SnapState{ 11992 Active: true, 11993 Sequence: []*snap.SideInfo{ 11994 {RealName: "some-snap", Revision: snap.R(11), SnapID: "the-core-ididididididididididid"}, 11995 }, 11996 Current: snap.R(11), 11997 SnapType: "os", 11998 }) 11999 12000 makeInstalledMockCoreSnap(c) 12001 12002 defls, err := snapstate.ConfigDefaults(s.state, deviceCtx, "core") 12003 c.Assert(err, IsNil) 12004 c.Assert(defls, DeepEquals, map[string]interface{}{"foo": "bar"}) 12005 } 12006 12007 var snapdSnapYaml = `name: snapd 12008 version: 1.0 12009 type: snapd 12010 ` 12011 12012 func (s *snapmgrTestSuite) TestConfigDefaultsSystemWithSnapdNoCore(c *C) { 12013 r := release.MockOnClassic(false) 12014 defer r() 12015 12016 // using MockSnapReadInfo, we want to read the bits on disk 12017 snapstate.MockSnapReadInfo(snap.ReadInfo) 12018 12019 s.state.Lock() 12020 defer s.state.Unlock() 12021 12022 s.prepareGadget(c, ` 12023 defaults: 12024 system: 12025 foo: bar 12026 `) 12027 12028 deviceCtx := &snapstatetest.TrivialDeviceContext{ 12029 DeviceModel: MakeModel(map[string]interface{}{ 12030 "gadget": "the-gadget", 12031 "base": "the-base", 12032 }), 12033 } 12034 12035 snapstate.Set(s.state, "core", nil) 12036 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 12037 Active: true, 12038 Sequence: []*snap.SideInfo{ 12039 {RealName: "snapd", SnapID: "the-snapd-snapidididididididididi", Revision: snap.R(1)}, 12040 }, 12041 Current: snap.R(1), 12042 SnapType: "snapd", 12043 }) 12044 12045 snaptest.MockSnap(c, snapdSnapYaml, &snap.SideInfo{ 12046 RealName: "snapd", 12047 Revision: snap.R(1), 12048 }) 12049 12050 defls, err := snapstate.ConfigDefaults(s.state, deviceCtx, "core") 12051 c.Assert(err, IsNil) 12052 c.Assert(defls, DeepEquals, map[string]interface{}{"foo": "bar"}) 12053 } 12054 12055 func (s *snapmgrTestSuite) TestConfigDefaultsSystemConflictsCoreSnapId(c *C) { 12056 r := release.MockOnClassic(false) 12057 defer r() 12058 12059 // using MockSnapReadInfo, we want to read the bits on disk 12060 snapstate.MockSnapReadInfo(snap.ReadInfo) 12061 12062 s.state.Lock() 12063 defer s.state.Unlock() 12064 12065 s.prepareGadget(c, ` 12066 defaults: 12067 system: 12068 foo: bar 12069 the-core-snapidididididididididi: 12070 foo: other-bar 12071 other-key: other-key-default 12072 `) 12073 12074 deviceCtx := deviceWithGadgetContext("the-gadget") 12075 12076 snapstate.Set(s.state, "core", &snapstate.SnapState{ 12077 Active: true, 12078 Sequence: []*snap.SideInfo{ 12079 {RealName: "core", SnapID: "the-core-snapidididididididididi", Revision: snap.R(1)}, 12080 }, 12081 Current: snap.R(1), 12082 SnapType: "os", 12083 }) 12084 12085 makeInstalledMockCoreSnap(c) 12086 12087 // 'system' key defaults take precedence over snap-id ones 12088 defls, err := snapstate.ConfigDefaults(s.state, deviceCtx, "core") 12089 c.Assert(err, IsNil) 12090 c.Assert(defls, DeepEquals, map[string]interface{}{"foo": "bar"}) 12091 } 12092 12093 func (s *snapmgrTestSuite) TestGadgetDefaultsAreNormalizedForConfigHook(c *C) { 12094 var mockGadgetSnapYaml = ` 12095 name: canonical-pc 12096 type: gadget 12097 ` 12098 var mockGadgetYaml = []byte(` 12099 defaults: 12100 otheridididididididididididididi: 12101 foo: 12102 bar: baz 12103 num: 1.305 12104 12105 volumes: 12106 volume-id: 12107 bootloader: grub 12108 `) 12109 12110 info := snaptest.MockSnap(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(2)}) 12111 err := ioutil.WriteFile(filepath.Join(info.MountDir(), "meta", "gadget.yaml"), mockGadgetYaml, 0644) 12112 c.Assert(err, IsNil) 12113 12114 gi, err := gadget.ReadInfo(info.MountDir(), false) 12115 c.Assert(err, IsNil) 12116 c.Assert(gi, NotNil) 12117 12118 snapName := "some-snap" 12119 hooksup := &hookstate.HookSetup{ 12120 Snap: snapName, 12121 Hook: "configure", 12122 Optional: true, 12123 IgnoreError: false, 12124 TrackError: false, 12125 } 12126 12127 var contextData map[string]interface{} 12128 contextData = map[string]interface{}{"patch": gi.Defaults} 12129 12130 s.state.Lock() 12131 defer s.state.Unlock() 12132 c.Assert(hookstate.HookTask(s.state, "", hooksup, contextData), NotNil) 12133 } 12134 12135 func makeInstalledMockCoreSnap(c *C) { 12136 coreSnapYaml := `name: core 12137 version: 1.0 12138 type: os 12139 ` 12140 snaptest.MockSnap(c, coreSnapYaml, &snap.SideInfo{ 12141 RealName: "core", 12142 Revision: snap.R(1), 12143 }) 12144 } 12145 12146 func (s *snapmgrTestSuite) TestGadgetDefaults(c *C) { 12147 r := release.MockOnClassic(false) 12148 defer r() 12149 12150 makeInstalledMockCoreSnap(c) 12151 12152 // using MockSnap, we want to read the bits on disk 12153 snapstate.MockSnapReadInfo(snap.ReadInfo) 12154 12155 s.state.Lock() 12156 defer s.state.Unlock() 12157 12158 s.prepareGadget(c) 12159 12160 snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0") 12161 12162 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{}) 12163 c.Assert(err, IsNil) 12164 12165 var m map[string]interface{} 12166 runHooks := tasksWithKind(ts, "run-hook") 12167 12168 c.Assert(taskKinds(runHooks), DeepEquals, []string{ 12169 "run-hook[install]", 12170 "run-hook[configure]", 12171 "run-hook[check-health]", 12172 }) 12173 err = runHooks[1].Get("hook-context", &m) 12174 c.Assert(err, IsNil) 12175 c.Assert(m, DeepEquals, map[string]interface{}{"use-defaults": true}) 12176 } 12177 12178 func (s *snapmgrTestSuite) TestGadgetDefaultsNotForOS(c *C) { 12179 r := release.MockOnClassic(false) 12180 defer r() 12181 12182 // using MockSnap, we want to read the bits on disk 12183 snapstate.MockSnapReadInfo(snap.ReadInfo) 12184 12185 s.state.Lock() 12186 defer s.state.Unlock() 12187 12188 snapstate.Set(s.state, "core", nil) 12189 12190 s.prepareGadget(c) 12191 12192 const coreSnapYaml = ` 12193 name: core 12194 type: os 12195 version: 1.0 12196 ` 12197 snapPath := makeTestSnap(c, coreSnapYaml) 12198 12199 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "core", SnapID: "core-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{}) 12200 c.Assert(err, IsNil) 12201 12202 var m map[string]interface{} 12203 runHooks := tasksWithKind(ts, "run-hook") 12204 12205 c.Assert(taskKinds(runHooks), DeepEquals, []string{ 12206 "run-hook[install]", 12207 "run-hook[configure]", 12208 "run-hook[check-health]", 12209 }) 12210 // use-defaults flag is part of hook-context which isn't set 12211 err = runHooks[1].Get("hook-context", &m) 12212 c.Assert(err, Equals, state.ErrNoState) 12213 } 12214 12215 func (s *snapmgrTestSuite) TestInstallPathSkipConfigure(c *C) { 12216 r := release.MockOnClassic(false) 12217 defer r() 12218 12219 makeInstalledMockCoreSnap(c) 12220 12221 // using MockSnap, we want to read the bits on disk 12222 snapstate.MockSnapReadInfo(snap.ReadInfo) 12223 12224 s.state.Lock() 12225 defer s.state.Unlock() 12226 12227 s.prepareGadget(c) 12228 12229 snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0") 12230 12231 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{SkipConfigure: true}) 12232 c.Assert(err, IsNil) 12233 12234 snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0]) 12235 c.Assert(err, IsNil) 12236 // SkipConfigure is consumed and consulted when creating the taskset 12237 // but is not copied into SnapSetup 12238 c.Check(snapsup.Flags.SkipConfigure, Equals, false) 12239 } 12240 12241 func (s *snapmgrTestSuite) TestNoReRefreshInUpdate(c *C) { 12242 s.state.Lock() 12243 defer s.state.Unlock() 12244 12245 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 12246 Active: true, 12247 Sequence: []*snap.SideInfo{ 12248 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 12249 }, 12250 Current: snap.R(1), 12251 SnapType: "app", 12252 }) 12253 12254 ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{NoReRefresh: true}) 12255 c.Assert(err, IsNil) 12256 12257 // ensure we have no re-refresh task 12258 for _, t := range ts.Tasks() { 12259 c.Assert(t.Kind(), Not(Equals), "check-rerefresh") 12260 } 12261 12262 snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0]) 12263 c.Assert(err, IsNil) 12264 // NoReRefresh is consumed and consulted when creating the taskset 12265 // but is not copied into SnapSetup 12266 c.Check(snapsup.Flags.NoReRefresh, Equals, false) 12267 } 12268 12269 func (s *snapmgrTestSuite) TestGadgetDefaultsInstalled(c *C) { 12270 makeInstalledMockCoreSnap(c) 12271 12272 // using MockSnap, we want to read the bits on disk 12273 snapstate.MockSnapReadInfo(snap.ReadInfo) 12274 12275 s.state.Lock() 12276 defer s.state.Unlock() 12277 12278 s.prepareGadget(c) 12279 12280 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 12281 Active: true, 12282 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}}, 12283 Current: snap.R(1), 12284 SnapType: "app", 12285 }) 12286 12287 snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0") 12288 12289 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, snapPath, "", "edge", snapstate.Flags{}) 12290 c.Assert(err, IsNil) 12291 12292 var m map[string]interface{} 12293 runHooks := tasksWithKind(ts, "run-hook") 12294 12295 c.Assert(runHooks[0].Kind(), Equals, "run-hook") 12296 err = runHooks[0].Get("hook-context", &m) 12297 c.Assert(err, Equals, state.ErrNoState) 12298 } 12299 12300 func (s *snapmgrTestSuite) TestTransitionCoreTasksNoUbuntuCore(c *C) { 12301 s.state.Lock() 12302 defer s.state.Unlock() 12303 12304 snapstate.Set(s.state, "core", &snapstate.SnapState{ 12305 Active: true, 12306 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}}, 12307 Current: snap.R(1), 12308 SnapType: "os", 12309 }) 12310 12311 _, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core") 12312 c.Assert(err, ErrorMatches, `cannot transition snap "ubuntu-core": not installed`) 12313 } 12314 12315 func verifyTransitionConnectionsTasks(c *C, ts *state.TaskSet) { 12316 c.Check(taskKinds(ts.Tasks()), DeepEquals, []string{ 12317 "transition-ubuntu-core", 12318 }) 12319 12320 transIf := ts.Tasks()[0] 12321 var oldName, newName string 12322 err := transIf.Get("old-name", &oldName) 12323 c.Assert(err, IsNil) 12324 c.Check(oldName, Equals, "ubuntu-core") 12325 12326 err = transIf.Get("new-name", &newName) 12327 c.Assert(err, IsNil) 12328 c.Check(newName, Equals, "core") 12329 } 12330 12331 func (s *snapmgrTestSuite) TestTransitionCoreTasks(c *C) { 12332 s.state.Lock() 12333 defer s.state.Unlock() 12334 12335 snapstate.Set(s.state, "core", nil) 12336 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 12337 Active: true, 12338 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 12339 Current: snap.R(1), 12340 SnapType: "os", 12341 }) 12342 12343 tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core") 12344 c.Assert(err, IsNil) 12345 12346 c.Assert(tsl, HasLen, 3) 12347 // 1. install core 12348 verifyInstallTasks(c, runCoreConfigure|maybeCore, 0, tsl[0], s.state) 12349 // 2 transition-connections 12350 verifyTransitionConnectionsTasks(c, tsl[1]) 12351 // 3 remove-ubuntu-core 12352 verifyCoreRemoveTasks(c, tsl[2]) 12353 } 12354 12355 func (s *snapmgrTestSuite) TestTransitionCoreTasksWithUbuntuCoreAndCore(c *C) { 12356 s.state.Lock() 12357 defer s.state.Unlock() 12358 12359 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 12360 Active: true, 12361 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 12362 Current: snap.R(1), 12363 SnapType: "os", 12364 }) 12365 snapstate.Set(s.state, "core", &snapstate.SnapState{ 12366 Active: true, 12367 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 12368 Current: snap.R(1), 12369 SnapType: "os", 12370 }) 12371 12372 tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core") 12373 c.Assert(err, IsNil) 12374 12375 c.Assert(tsl, HasLen, 2) 12376 // 1. transition connections 12377 verifyTransitionConnectionsTasks(c, tsl[0]) 12378 // 2. remove ubuntu-core 12379 verifyCoreRemoveTasks(c, tsl[1]) 12380 } 12381 12382 func (s *snapmgrTestSuite) TestTransitionCoreRunThrough(c *C) { 12383 s.state.Lock() 12384 defer s.state.Unlock() 12385 12386 snapstate.Set(s.state, "core", nil) 12387 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 12388 Active: true, 12389 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 12390 Current: snap.R(1), 12391 SnapType: "os", 12392 Channel: "beta", 12393 }) 12394 12395 chg := s.state.NewChange("transition-ubuntu-core", "...") 12396 tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core") 12397 c.Assert(err, IsNil) 12398 for _, ts := range tsl { 12399 chg.AddAll(ts) 12400 } 12401 12402 s.state.Unlock() 12403 defer s.se.Stop() 12404 s.settle(c) 12405 s.state.Lock() 12406 12407 // ensure all our tasks ran 12408 c.Assert(chg.Err(), IsNil) 12409 c.Assert(chg.IsReady(), Equals, true) 12410 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 12411 name: "core", 12412 // the transition has no user associcated with it 12413 macaroon: "", 12414 target: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 12415 }}) 12416 expected := fakeOps{ 12417 { 12418 op: "storesvc-snap-action", 12419 curSnaps: []store.CurrentSnap{ 12420 { 12421 InstanceName: "ubuntu-core", 12422 SnapID: "ubuntu-core-snap-id", 12423 Revision: snap.R(1), 12424 TrackingChannel: "beta", 12425 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1), 12426 Epoch: snap.E("1*"), 12427 }, 12428 }, 12429 }, 12430 { 12431 op: "storesvc-snap-action:action", 12432 action: store.SnapAction{ 12433 Action: "install", 12434 InstanceName: "core", 12435 Channel: "beta", 12436 }, 12437 revno: snap.R(11), 12438 }, 12439 { 12440 op: "storesvc-download", 12441 name: "core", 12442 }, 12443 { 12444 op: "validate-snap:Doing", 12445 name: "core", 12446 revno: snap.R(11), 12447 }, 12448 { 12449 op: "current", 12450 old: "<no-current>", 12451 }, 12452 { 12453 op: "open-snap-file", 12454 path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 12455 sinfo: snap.SideInfo{ 12456 RealName: "core", 12457 SnapID: "core-id", 12458 Channel: "beta", 12459 Revision: snap.R(11), 12460 }, 12461 }, 12462 { 12463 op: "setup-snap", 12464 name: "core", 12465 path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 12466 revno: snap.R(11), 12467 }, 12468 { 12469 op: "copy-data", 12470 path: filepath.Join(dirs.SnapMountDir, "core/11"), 12471 old: "<no-old>", 12472 }, 12473 { 12474 op: "setup-profiles:Doing", 12475 name: "core", 12476 revno: snap.R(11), 12477 }, 12478 { 12479 op: "candidate", 12480 sinfo: snap.SideInfo{ 12481 RealName: "core", 12482 SnapID: "core-id", 12483 Channel: "beta", 12484 Revision: snap.R(11), 12485 }, 12486 }, 12487 { 12488 op: "link-snap", 12489 path: filepath.Join(dirs.SnapMountDir, "core/11"), 12490 }, 12491 { 12492 op: "auto-connect:Doing", 12493 name: "core", 12494 revno: snap.R(11), 12495 }, 12496 { 12497 op: "update-aliases", 12498 }, 12499 { 12500 op: "transition-ubuntu-core:Doing", 12501 name: "ubuntu-core", 12502 }, 12503 { 12504 op: "auto-disconnect:Doing", 12505 name: "ubuntu-core", 12506 revno: snap.R(1), 12507 }, 12508 { 12509 op: "remove-snap-aliases", 12510 name: "ubuntu-core", 12511 }, 12512 { 12513 op: "unlink-snap", 12514 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 12515 }, 12516 { 12517 op: "remove-profiles:Doing", 12518 name: "ubuntu-core", 12519 revno: snap.R(1), 12520 }, 12521 { 12522 op: "remove-snap-data", 12523 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 12524 }, 12525 { 12526 op: "remove-snap-common-data", 12527 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 12528 }, 12529 { 12530 op: "remove-snap-data-dir", 12531 name: "ubuntu-core", 12532 path: filepath.Join(dirs.SnapDataDir, "ubuntu-core"), 12533 }, 12534 { 12535 op: "remove-snap-files", 12536 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 12537 stype: "os", 12538 }, 12539 { 12540 op: "discard-namespace", 12541 name: "ubuntu-core", 12542 }, 12543 { 12544 op: "remove-snap-dir", 12545 name: "ubuntu-core", 12546 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core"), 12547 }, 12548 { 12549 op: "cleanup-trash", 12550 name: "core", 12551 revno: snap.R(11), 12552 }, 12553 } 12554 // start with an easier-to-read error if this fails: 12555 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 12556 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 12557 } 12558 12559 func (s *snapmgrTestSuite) TestTransitionCoreRunThroughWithCore(c *C) { 12560 s.state.Lock() 12561 defer s.state.Unlock() 12562 12563 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 12564 Active: true, 12565 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 12566 Current: snap.R(1), 12567 SnapType: "os", 12568 Channel: "stable", 12569 }) 12570 snapstate.Set(s.state, "core", &snapstate.SnapState{ 12571 Active: true, 12572 Sequence: []*snap.SideInfo{{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(1)}}, 12573 Current: snap.R(1), 12574 SnapType: "os", 12575 Channel: "stable", 12576 }) 12577 12578 chg := s.state.NewChange("transition-ubuntu-core", "...") 12579 tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core") 12580 c.Assert(err, IsNil) 12581 for _, ts := range tsl { 12582 chg.AddAll(ts) 12583 } 12584 12585 s.state.Unlock() 12586 defer s.se.Stop() 12587 s.settle(c) 12588 s.state.Lock() 12589 12590 // ensure all our tasks ran 12591 c.Assert(chg.Err(), IsNil) 12592 c.Assert(chg.IsReady(), Equals, true) 12593 c.Check(s.fakeStore.downloads, HasLen, 0) 12594 expected := fakeOps{ 12595 { 12596 op: "transition-ubuntu-core:Doing", 12597 name: "ubuntu-core", 12598 }, 12599 { 12600 op: "auto-disconnect:Doing", 12601 name: "ubuntu-core", 12602 revno: snap.R(1), 12603 }, 12604 { 12605 op: "remove-snap-aliases", 12606 name: "ubuntu-core", 12607 }, 12608 { 12609 op: "unlink-snap", 12610 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 12611 }, 12612 { 12613 op: "remove-profiles:Doing", 12614 name: "ubuntu-core", 12615 revno: snap.R(1), 12616 }, 12617 { 12618 op: "remove-snap-data", 12619 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 12620 }, 12621 { 12622 op: "remove-snap-common-data", 12623 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 12624 }, 12625 { 12626 op: "remove-snap-data-dir", 12627 name: "ubuntu-core", 12628 path: filepath.Join(dirs.SnapDataDir, "ubuntu-core"), 12629 }, 12630 { 12631 op: "remove-snap-files", 12632 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 12633 stype: "os", 12634 }, 12635 { 12636 op: "discard-namespace", 12637 name: "ubuntu-core", 12638 }, 12639 { 12640 op: "remove-snap-dir", 12641 name: "ubuntu-core", 12642 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core"), 12643 }, 12644 } 12645 // start with an easier-to-read error if this fails: 12646 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 12647 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 12648 } 12649 12650 func (s *snapmgrTestSuite) TestTransitionCoreStartsAutomatically(c *C) { 12651 s.state.Lock() 12652 defer s.state.Unlock() 12653 12654 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 12655 Active: true, 12656 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}}, 12657 Current: snap.R(1), 12658 SnapType: "os", 12659 }) 12660 12661 s.state.Unlock() 12662 defer s.se.Stop() 12663 s.settle(c) 12664 s.state.Lock() 12665 12666 c.Check(s.state.Changes(), HasLen, 1) 12667 c.Check(s.state.Changes()[0].Kind(), Equals, "transition-ubuntu-core") 12668 } 12669 12670 func (s *snapmgrTestSuite) TestTransitionCoreTooEarly(c *C) { 12671 s.state.Lock() 12672 defer s.state.Unlock() 12673 12674 r := snapstatetest.MockDeviceModel(nil) 12675 defer r() 12676 12677 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 12678 Active: true, 12679 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}}, 12680 Current: snap.R(1), 12681 SnapType: "os", 12682 }) 12683 12684 s.state.Unlock() 12685 defer s.se.Stop() 12686 s.settle(c) 12687 s.state.Lock() 12688 12689 c.Check(s.state.Changes(), HasLen, 0) 12690 // not counted as a try 12691 var t time.Time 12692 err := s.state.Get("ubuntu-core-transition-last-retry-time", &t) 12693 c.Assert(err, Equals, state.ErrNoState) 12694 } 12695 12696 func (s *snapmgrTestSuite) TestTransitionCoreTimeLimitWorks(c *C) { 12697 s.state.Lock() 12698 defer s.state.Unlock() 12699 12700 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 12701 Active: true, 12702 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}}, 12703 Current: snap.R(1), 12704 SnapType: "os", 12705 }) 12706 12707 // tried 3h ago, no retry 12708 s.state.Set("ubuntu-core-transition-last-retry-time", time.Now().Add(-3*time.Hour)) 12709 12710 s.state.Unlock() 12711 defer s.se.Stop() 12712 s.settle(c) 12713 s.state.Lock() 12714 12715 c.Check(s.state.Changes(), HasLen, 0) 12716 12717 // tried 7h ago, retry 12718 s.state.Set("ubuntu-core-transition-last-retry-time", time.Now().Add(-7*time.Hour)) 12719 12720 s.state.Unlock() 12721 defer s.se.Stop() 12722 s.settle(c) 12723 s.state.Lock() 12724 c.Check(s.state.Changes(), HasLen, 1) 12725 12726 var t time.Time 12727 s.state.Get("ubuntu-core-transition-last-retry-time", &t) 12728 c.Assert(time.Now().Sub(t) < 2*time.Minute, Equals, true) 12729 } 12730 12731 func (s *snapmgrTestSuite) TestTransitionCoreNoOtherChanges(c *C) { 12732 s.state.Lock() 12733 defer s.state.Unlock() 12734 12735 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 12736 Active: true, 12737 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}}, 12738 Current: snap.R(1), 12739 SnapType: "os", 12740 }) 12741 chg := s.state.NewChange("unrelated-change", "unfinished change blocks core transition") 12742 chg.SetStatus(state.DoStatus) 12743 12744 s.state.Unlock() 12745 defer s.se.Stop() 12746 s.settle(c) 12747 s.state.Lock() 12748 12749 c.Check(s.state.Changes(), HasLen, 1) 12750 c.Check(s.state.Changes()[0].Kind(), Equals, "unrelated-change") 12751 } 12752 12753 func (s *snapmgrTestSuite) TestTransitionCoreBlocksOtherChanges(c *C) { 12754 s.state.Lock() 12755 defer s.state.Unlock() 12756 12757 // if we have a ubuntu-core -> core transition 12758 chg := s.state.NewChange("transition-ubuntu-core", "...") 12759 chg.SetStatus(state.DoStatus) 12760 12761 // other tasks block until the transition is done 12762 opts := &snapstate.RevisionOptions{Channel: "stable"} 12763 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 12764 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 12765 c.Check(err, ErrorMatches, "ubuntu-core to core transition in progress, no other changes allowed until this is done") 12766 12767 // and when the transition is done, other tasks run 12768 chg.SetStatus(state.DoneStatus) 12769 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 12770 c.Check(err, IsNil) 12771 c.Check(ts, NotNil) 12772 } 12773 12774 func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesNotRunWithoutSnaps(c *C) { 12775 s.state.Lock() 12776 defer s.state.Unlock() 12777 12778 tr := config.NewTransaction(s.state) 12779 tr.Set("core", "experimental.snapd-snap", true) 12780 tr.Commit() 12781 12782 // no snaps installed on this system (e.g. fresh classic) 12783 snapstate.Set(s.state, "core", nil) 12784 12785 s.state.Unlock() 12786 defer s.se.Stop() 12787 s.settle(c) 12788 s.state.Lock() 12789 12790 c.Check(s.state.Changes(), HasLen, 0) 12791 } 12792 12793 func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesRunWithAnySnap(c *C) { 12794 s.state.Lock() 12795 defer s.state.Unlock() 12796 12797 tr := config.NewTransaction(s.state) 12798 tr.Set("core", "experimental.snapd-snap", true) 12799 tr.Commit() 12800 12801 // some snap installed on this system but no core 12802 snapstate.Set(s.state, "core", nil) 12803 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 12804 Active: true, 12805 Sequence: []*snap.SideInfo{{RealName: "foo", SnapID: "foo-id", Revision: snap.R(1), Channel: "beta"}}, 12806 Current: snap.R(1), 12807 }) 12808 12809 s.state.Unlock() 12810 defer s.se.Stop() 12811 s.settle(c) 12812 s.state.Lock() 12813 12814 c.Check(s.state.Changes(), HasLen, 1) 12815 } 12816 12817 func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesNotRunWhenNotEnabled(c *C) { 12818 s.state.Lock() 12819 defer s.state.Unlock() 12820 12821 snapstate.Set(s.state, "core", &snapstate.SnapState{ 12822 Active: true, 12823 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "beta"}}, 12824 Current: snap.R(1), 12825 SnapType: "os", 12826 }) 12827 12828 s.state.Unlock() 12829 defer s.se.Stop() 12830 s.settle(c) 12831 s.state.Lock() 12832 12833 c.Check(s.state.Changes(), HasLen, 0) 12834 } 12835 12836 func (s *snapmgrTestSuite) TestTransitionSnapdSnapStartsAutomaticallyWhenEnabled(c *C) { 12837 s.state.Lock() 12838 defer s.state.Unlock() 12839 12840 snapstate.Set(s.state, "core", &snapstate.SnapState{ 12841 Active: true, 12842 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "beta"}}, 12843 Current: snap.R(1), 12844 SnapType: "os", 12845 }) 12846 tr := config.NewTransaction(s.state) 12847 tr.Set("core", "experimental.snapd-snap", true) 12848 tr.Commit() 12849 12850 s.state.Unlock() 12851 defer s.se.Stop() 12852 s.settle(c) 12853 s.state.Lock() 12854 12855 c.Check(s.state.Changes(), HasLen, 1) 12856 chg := s.state.Changes()[0] 12857 c.Check(chg.Kind(), Equals, "transition-to-snapd-snap") 12858 c.Assert(chg.Err(), IsNil) 12859 c.Assert(chg.IsReady(), Equals, true) 12860 12861 // snapd snap is instaleld from the default channel 12862 var snapst snapstate.SnapState 12863 snapstate.Get(s.state, "snapd", &snapst) 12864 c.Assert(snapst.Channel, Equals, "stable") 12865 } 12866 12867 func (s *snapmgrTestSuite) TestTransitionSnapdSnapWithCoreRunthrough(c *C) { 12868 s.state.Lock() 12869 defer s.state.Unlock() 12870 12871 snapstate.Set(s.state, "core", &snapstate.SnapState{ 12872 Active: true, 12873 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "edge"}}, 12874 Current: snap.R(1), 12875 SnapType: "os", 12876 // TrackingChannel 12877 Channel: "beta", 12878 }) 12879 tr := config.NewTransaction(s.state) 12880 tr.Set("core", "experimental.snapd-snap", true) 12881 tr.Commit() 12882 12883 s.state.Unlock() 12884 defer s.se.Stop() 12885 s.settle(c) 12886 s.state.Lock() 12887 12888 c.Assert(s.state.Changes(), HasLen, 1) 12889 chg := s.state.Changes()[0] 12890 c.Assert(chg.Kind(), Equals, "transition-to-snapd-snap") 12891 c.Assert(chg.Err(), IsNil) 12892 c.Assert(chg.IsReady(), Equals, true) 12893 c.Check(s.fakeStore.downloads, HasLen, 1) 12894 ts := state.NewTaskSet(chg.Tasks()...) 12895 verifyInstallTasks(c, noConfigure, 0, ts, s.state) 12896 12897 // ensure preferences from the core snap got transferred over 12898 var snapst snapstate.SnapState 12899 snapstate.Get(s.state, "snapd", &snapst) 12900 c.Assert(snapst.Channel, Equals, "beta") 12901 } 12902 12903 func (s *snapmgrTestSuite) TestTransitionSnapdSnapTimeLimitWorks(c *C) { 12904 s.state.Lock() 12905 defer s.state.Unlock() 12906 12907 tr := config.NewTransaction(s.state) 12908 tr.Set("core", "experimental.snapd-snap", true) 12909 tr.Commit() 12910 12911 // tried 3h ago, no retry 12912 s.state.Set("snapd-transition-last-retry-time", time.Now().Add(-3*time.Hour)) 12913 12914 s.state.Unlock() 12915 defer s.se.Stop() 12916 s.settle(c) 12917 s.state.Lock() 12918 12919 c.Check(s.state.Changes(), HasLen, 0) 12920 12921 // tried 7h ago, retry 12922 s.state.Set("snapd-transition-last-retry-time", time.Now().Add(-7*time.Hour)) 12923 12924 s.state.Unlock() 12925 defer s.se.Stop() 12926 s.settle(c) 12927 s.state.Lock() 12928 c.Check(s.state.Changes(), HasLen, 1) 12929 12930 var t time.Time 12931 s.state.Get("snapd-transition-last-retry-time", &t) 12932 c.Assert(time.Now().Sub(t) < 2*time.Minute, Equals, true) 12933 } 12934 12935 type unhappyStore struct { 12936 *fakeStore 12937 } 12938 12939 func (s unhappyStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) { 12940 12941 return nil, fmt.Errorf("a grumpy store") 12942 } 12943 12944 func (s *snapmgrTestSuite) TestTransitionSnapdSnapError(c *C) { 12945 s.state.Lock() 12946 defer s.state.Unlock() 12947 12948 snapstate.ReplaceStore(s.state, unhappyStore{fakeStore: s.fakeStore}) 12949 12950 tr := config.NewTransaction(s.state) 12951 tr.Set("core", "experimental.snapd-snap", true) 12952 tr.Commit() 12953 12954 s.state.Unlock() 12955 defer s.se.Stop() 12956 err := s.o.Settle(5 * time.Second) 12957 c.Assert(err, ErrorMatches, `state ensure errors: \[a grumpy store\]`) 12958 12959 s.state.Lock() 12960 c.Check(s.state.Changes(), HasLen, 0) 12961 12962 // all the attempts were recorded 12963 var t time.Time 12964 s.state.Get("snapd-transition-last-retry-time", &t) 12965 c.Assert(time.Now().Sub(t) < 2*time.Minute, Equals, true) 12966 12967 var cnt int 12968 s.state.Get("snapd-transition-retry", &cnt) 12969 c.Assert(cnt, Equals, 1) 12970 12971 // the transition is not tried again (because of retry time) 12972 s.state.Unlock() 12973 err = s.o.Settle(5 * time.Second) 12974 c.Assert(err, IsNil) 12975 s.state.Lock() 12976 12977 s.state.Get("snapd-transition-retry", &cnt) 12978 c.Assert(cnt, Equals, 1) 12979 } 12980 12981 func (s *snapmgrTestSuite) TestTransitionSnapdSnapBlocksOtherChanges(c *C) { 12982 s.state.Lock() 12983 defer s.state.Unlock() 12984 12985 // if we have a snapd transition 12986 chg := s.state.NewChange("transition-to-snapd-snap", "...") 12987 chg.SetStatus(state.DoStatus) 12988 12989 // other tasks block until the transition is done 12990 _, err := snapstate.Install(context.Background(), s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{}) 12991 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 12992 c.Check(err, ErrorMatches, "transition to snapd snap in progress, no other changes allowed until this is done") 12993 12994 // and when the transition is done, other tasks run 12995 chg.SetStatus(state.DoneStatus) 12996 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{}) 12997 c.Check(err, IsNil) 12998 c.Check(ts, NotNil) 12999 } 13000 13001 func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsForUbuntuCore(c *C) { 13002 s.checkForceDevModeCleanupRuns(c, "ubuntu-core", true) 13003 } 13004 13005 func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsForCore(c *C) { 13006 s.checkForceDevModeCleanupRuns(c, "core", true) 13007 } 13008 13009 func (s *snapmgrTestSuite) TestForceDevModeCleanupSkipsRando(c *C) { 13010 s.checkForceDevModeCleanupRuns(c, "rando", false) 13011 } 13012 13013 func (s *snapmgrTestSuite) checkForceDevModeCleanupRuns(c *C, name string, shouldBeReset bool) { 13014 r := release.MockForcedDevmode(true) 13015 defer r() 13016 c.Assert(release.ReleaseInfo.ForceDevMode(), Equals, true) 13017 13018 s.state.Lock() 13019 defer s.state.Unlock() 13020 13021 snapstate.Set(s.state, name, &snapstate.SnapState{ 13022 Active: true, 13023 Sequence: []*snap.SideInfo{{ 13024 RealName: name, 13025 SnapID: "id-id-id", 13026 Revision: snap.R(1)}}, 13027 Current: snap.R(1), 13028 SnapType: "os", 13029 Flags: snapstate.Flags{DevMode: true}, 13030 }) 13031 13032 var snapst1 snapstate.SnapState 13033 // sanity check 13034 snapstate.Get(s.state, name, &snapst1) 13035 c.Assert(snapst1.DevMode, Equals, true) 13036 13037 s.state.Unlock() 13038 defer s.se.Stop() 13039 s.settle(c) 13040 s.state.Lock() 13041 13042 var snapst2 snapstate.SnapState 13043 snapstate.Get(s.state, name, &snapst2) 13044 13045 c.Check(snapst2.DevMode, Equals, !shouldBeReset) 13046 13047 var n int 13048 s.state.Get("fix-forced-devmode", &n) 13049 c.Check(n, Equals, 1) 13050 } 13051 13052 func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsNoSnaps(c *C) { 13053 r := release.MockForcedDevmode(true) 13054 defer r() 13055 c.Assert(release.ReleaseInfo.ForceDevMode(), Equals, true) 13056 13057 defer s.se.Stop() 13058 s.settle(c) 13059 s.state.Lock() 13060 defer s.state.Unlock() 13061 13062 var n int 13063 s.state.Get("fix-forced-devmode", &n) 13064 c.Check(n, Equals, 1) 13065 } 13066 13067 func (s *snapmgrTestSuite) TestForceDevModeCleanupSkipsNonForcedOS(c *C) { 13068 r := release.MockForcedDevmode(false) 13069 defer r() 13070 c.Assert(release.ReleaseInfo.ForceDevMode(), Equals, false) 13071 13072 s.state.Lock() 13073 defer s.state.Unlock() 13074 13075 snapstate.Set(s.state, "core", &snapstate.SnapState{ 13076 Active: true, 13077 Sequence: []*snap.SideInfo{{ 13078 RealName: "core", 13079 SnapID: "id-id-id", 13080 Revision: snap.R(1)}}, 13081 Current: snap.R(1), 13082 SnapType: "os", 13083 Flags: snapstate.Flags{DevMode: true}, 13084 }) 13085 13086 var snapst1 snapstate.SnapState 13087 // sanity check 13088 snapstate.Get(s.state, "core", &snapst1) 13089 c.Assert(snapst1.DevMode, Equals, true) 13090 13091 s.state.Unlock() 13092 defer s.se.Stop() 13093 s.settle(c) 13094 s.state.Lock() 13095 13096 var snapst2 snapstate.SnapState 13097 snapstate.Get(s.state, "core", &snapst2) 13098 13099 // no change 13100 c.Check(snapst2.DevMode, Equals, true) 13101 13102 // not really run at all in fact 13103 var n int 13104 s.state.Get("fix-forced-devmode", &n) 13105 c.Check(n, Equals, 0) 13106 } 13107 13108 func (s *snapmgrTestSuite) TestEnsureAliasesV2(c *C) { 13109 s.state.Lock() 13110 defer s.state.Unlock() 13111 13112 snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) { 13113 switch info.InstanceName() { 13114 case "alias-snap": 13115 return map[string]string{ 13116 "alias1": "cmd1", 13117 "alias2": "cmd2", 13118 }, nil 13119 } 13120 return nil, nil 13121 } 13122 13123 snapstate.Set(s.state, "core", nil) 13124 snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{ 13125 Sequence: []*snap.SideInfo{ 13126 {RealName: "alias-snap", Revision: snap.R(11)}, 13127 }, 13128 Current: snap.R(11), 13129 Active: true, 13130 }) 13131 13132 s.state.Set("aliases", map[string]map[string]string{ 13133 "alias-snap": { 13134 "alias1": "auto", 13135 }, 13136 }) 13137 13138 s.state.Unlock() 13139 err := s.snapmgr.Ensure() 13140 s.state.Lock() 13141 c.Assert(err, IsNil) 13142 13143 var gone interface{} 13144 err = s.state.Get("aliases", &gone) 13145 c.Assert(err, Equals, state.ErrNoState) 13146 13147 var snapst snapstate.SnapState 13148 err = snapstate.Get(s.state, "alias-snap", &snapst) 13149 c.Assert(err, IsNil) 13150 13151 c.Check(snapst.AutoAliasesDisabled, Equals, false) 13152 c.Check(snapst.AliasesPending, Equals, false) 13153 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 13154 "alias1": {Auto: "cmd1"}, 13155 "alias2": {Auto: "cmd2"}, 13156 }) 13157 13158 expected := fakeOps{ 13159 { 13160 op: "remove-snap-aliases", 13161 name: "alias-snap", 13162 }, 13163 { 13164 op: "update-aliases", 13165 aliases: []*backend.Alias{ 13166 {Name: "alias1", Target: "alias-snap.cmd1"}, 13167 {Name: "alias2", Target: "alias-snap.cmd2"}, 13168 }, 13169 }, 13170 } 13171 // start with an easier-to-read error if this fails: 13172 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 13173 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 13174 } 13175 13176 func (s *snapmgrTestSuite) TestEnsureAliasesV2SnapDisabled(c *C) { 13177 s.state.Lock() 13178 defer s.state.Unlock() 13179 13180 snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) { 13181 switch info.InstanceName() { 13182 case "alias-snap": 13183 return map[string]string{ 13184 "alias1": "cmd1", 13185 "alias2": "cmd2", 13186 }, nil 13187 } 13188 return nil, nil 13189 } 13190 13191 snapstate.Set(s.state, "core", nil) 13192 snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{ 13193 Sequence: []*snap.SideInfo{ 13194 {RealName: "alias-snap", Revision: snap.R(11)}, 13195 }, 13196 Current: snap.R(11), 13197 Active: false, 13198 }) 13199 13200 s.state.Set("aliases", map[string]map[string]string{ 13201 "alias-snap": { 13202 "alias1": "auto", 13203 }, 13204 }) 13205 13206 s.state.Unlock() 13207 err := s.snapmgr.Ensure() 13208 s.state.Lock() 13209 c.Assert(err, IsNil) 13210 13211 var gone interface{} 13212 err = s.state.Get("aliases", &gone) 13213 c.Assert(err, Equals, state.ErrNoState) 13214 13215 var snapst snapstate.SnapState 13216 err = snapstate.Get(s.state, "alias-snap", &snapst) 13217 c.Assert(err, IsNil) 13218 13219 c.Check(snapst.AutoAliasesDisabled, Equals, false) 13220 c.Check(snapst.AliasesPending, Equals, true) 13221 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 13222 "alias1": {Auto: "cmd1"}, 13223 "alias2": {Auto: "cmd2"}, 13224 }) 13225 13226 expected := fakeOps{ 13227 { 13228 op: "remove-snap-aliases", 13229 name: "alias-snap", 13230 }, 13231 } 13232 // start with an easier-to-read error if this fails: 13233 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 13234 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 13235 } 13236 13237 func (s *snapmgrTestSuite) TestEnsureAliasesV2MarkAliasTasksInError(c *C) { 13238 s.state.Lock() 13239 defer s.state.Unlock() 13240 13241 s.state.Set("aliases", map[string]map[string]string{ 13242 "alias-snap": { 13243 "alias1": "auto", 13244 }, 13245 }) 13246 13247 // pending old alias task 13248 t := s.state.NewTask("alias", "...") 13249 t.Set("aliases", map[string]string{}) 13250 chg := s.state.NewChange("alias chg", "...") 13251 chg.AddTask(t) 13252 13253 s.state.Unlock() 13254 err := s.snapmgr.Ensure() 13255 s.state.Lock() 13256 c.Assert(err, IsNil) 13257 13258 c.Check(chg.Status(), Equals, state.ErrorStatus) 13259 c.Check(chg.IsReady(), Equals, true) 13260 c.Check(t.Status(), Equals, state.ErrorStatus) 13261 } 13262 13263 func (s *snapmgrTestSuite) TestConflictMany(c *C) { 13264 s.state.Lock() 13265 defer s.state.Unlock() 13266 13267 for _, instanceName := range []string{"a-snap", "b-snap"} { 13268 snapstate.Set(s.state, instanceName, &snapstate.SnapState{ 13269 Sequence: []*snap.SideInfo{ 13270 {RealName: instanceName, Revision: snap.R(11)}, 13271 }, 13272 Current: snap.R(11), 13273 Active: false, 13274 }) 13275 13276 ts, err := snapstate.Enable(s.state, instanceName) 13277 c.Assert(err, IsNil) 13278 // need a change to make the tasks visible 13279 s.state.NewChange("enable", "...").AddAll(ts) 13280 } 13281 13282 // things that should be ok: 13283 for _, m := range [][]string{ 13284 {}, //nothing 13285 {"c-snap"}, 13286 {"c-snap", "d-snap", "e-snap", "f-snap"}, 13287 } { 13288 c.Check(snapstate.CheckChangeConflictMany(s.state, m, ""), IsNil) 13289 } 13290 13291 // things that should not be ok: 13292 for _, m := range [][]string{ 13293 {"a-snap"}, 13294 {"a-snap", "b-snap"}, 13295 {"a-snap", "c-snap"}, 13296 {"b-snap", "c-snap"}, 13297 } { 13298 err := snapstate.CheckChangeConflictMany(s.state, m, "") 13299 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 13300 c.Check(err, ErrorMatches, `snap "[^"]*" has "enable" change in progress`) 13301 } 13302 } 13303 13304 func (s *snapmgrTestSuite) TestConflictManyRemodeling(c *C) { 13305 s.state.Lock() 13306 defer s.state.Unlock() 13307 13308 chg := s.state.NewChange("remodel", "...") 13309 chg.SetStatus(state.DoingStatus) 13310 13311 err := snapstate.CheckChangeConflictMany(s.state, []string{"a-snap"}, "") 13312 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 13313 c.Check(err, ErrorMatches, `remodeling in progress, no other changes allowed until this is done`) 13314 } 13315 13316 func (s *snapmgrTestSuite) TestInstallWithoutCoreRunThrough1(c *C) { 13317 s.state.Lock() 13318 defer s.state.Unlock() 13319 13320 // pretend we don't have core 13321 snapstate.Set(s.state, "core", nil) 13322 13323 chg := s.state.NewChange("install", "install a snap on a system without core") 13324 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 13325 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 13326 c.Assert(err, IsNil) 13327 chg.AddAll(ts) 13328 13329 s.state.Unlock() 13330 defer s.se.Stop() 13331 s.settle(c) 13332 s.state.Lock() 13333 13334 // ensure all our tasks ran 13335 c.Assert(chg.Err(), IsNil) 13336 c.Assert(chg.IsReady(), Equals, true) 13337 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{ 13338 { 13339 macaroon: s.user.StoreMacaroon, 13340 name: "core", 13341 target: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 13342 }, 13343 { 13344 macaroon: s.user.StoreMacaroon, 13345 name: "some-snap", 13346 target: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 13347 }}) 13348 expected := fakeOps{ 13349 // we check the snap 13350 { 13351 op: "storesvc-snap-action", 13352 userID: 1, 13353 }, 13354 { 13355 op: "storesvc-snap-action:action", 13356 action: store.SnapAction{ 13357 Action: "install", 13358 InstanceName: "some-snap", 13359 Revision: snap.R(42), 13360 }, 13361 revno: snap.R(42), 13362 userID: 1, 13363 }, 13364 // then we check core because its not installed already 13365 // and continue with that 13366 { 13367 op: "storesvc-snap-action", 13368 userID: 1, 13369 }, 13370 { 13371 op: "storesvc-snap-action:action", 13372 action: store.SnapAction{ 13373 Action: "install", 13374 InstanceName: "core", 13375 Channel: "stable", 13376 }, 13377 revno: snap.R(11), 13378 userID: 1, 13379 }, 13380 { 13381 op: "storesvc-download", 13382 name: "core", 13383 }, 13384 { 13385 op: "validate-snap:Doing", 13386 name: "core", 13387 revno: snap.R(11), 13388 }, 13389 { 13390 op: "current", 13391 old: "<no-current>", 13392 }, 13393 { 13394 op: "open-snap-file", 13395 path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 13396 sinfo: snap.SideInfo{ 13397 RealName: "core", 13398 Channel: "stable", 13399 SnapID: "core-id", 13400 Revision: snap.R(11), 13401 }, 13402 }, 13403 { 13404 op: "setup-snap", 13405 name: "core", 13406 path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 13407 revno: snap.R(11), 13408 }, 13409 { 13410 op: "copy-data", 13411 path: filepath.Join(dirs.SnapMountDir, "core/11"), 13412 old: "<no-old>", 13413 }, 13414 { 13415 op: "setup-profiles:Doing", 13416 name: "core", 13417 revno: snap.R(11), 13418 }, 13419 { 13420 op: "candidate", 13421 sinfo: snap.SideInfo{ 13422 RealName: "core", 13423 Channel: "stable", 13424 SnapID: "core-id", 13425 Revision: snap.R(11), 13426 }, 13427 }, 13428 { 13429 op: "link-snap", 13430 path: filepath.Join(dirs.SnapMountDir, "core/11"), 13431 }, 13432 { 13433 op: "auto-connect:Doing", 13434 name: "core", 13435 revno: snap.R(11), 13436 }, 13437 { 13438 op: "update-aliases", 13439 }, 13440 // after core is in place continue with the snap 13441 { 13442 op: "storesvc-download", 13443 name: "some-snap", 13444 }, 13445 { 13446 op: "validate-snap:Doing", 13447 name: "some-snap", 13448 revno: snap.R(42), 13449 }, 13450 { 13451 op: "current", 13452 old: "<no-current>", 13453 }, 13454 { 13455 op: "open-snap-file", 13456 path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 13457 sinfo: snap.SideInfo{ 13458 RealName: "some-snap", 13459 SnapID: "some-snap-id", 13460 Revision: snap.R(42), 13461 }, 13462 }, 13463 { 13464 op: "setup-snap", 13465 name: "some-snap", 13466 path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 13467 revno: snap.R(42), 13468 }, 13469 { 13470 op: "copy-data", 13471 path: filepath.Join(dirs.SnapMountDir, "some-snap/42"), 13472 old: "<no-old>", 13473 }, 13474 { 13475 op: "setup-profiles:Doing", 13476 name: "some-snap", 13477 revno: snap.R(42), 13478 }, 13479 { 13480 op: "candidate", 13481 sinfo: snap.SideInfo{ 13482 RealName: "some-snap", 13483 SnapID: "some-snap-id", 13484 Revision: snap.R(42), 13485 }, 13486 }, 13487 { 13488 op: "link-snap", 13489 path: filepath.Join(dirs.SnapMountDir, "some-snap/42"), 13490 }, 13491 { 13492 op: "auto-connect:Doing", 13493 name: "some-snap", 13494 revno: snap.R(42), 13495 }, 13496 { 13497 op: "update-aliases", 13498 }, 13499 // cleanups order is random 13500 { 13501 op: "cleanup-trash", 13502 name: "core", 13503 revno: snap.R(42), 13504 }, 13505 { 13506 op: "cleanup-trash", 13507 name: "some-snap", 13508 revno: snap.R(42), 13509 }, 13510 } 13511 // start with an easier-to-read error if this fails: 13512 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 13513 // compare the details without the cleanup tasks, the order is random 13514 // as they run in parallel 13515 opsLenWithoutCleanups := len(s.fakeBackend.ops) - 2 13516 c.Assert(s.fakeBackend.ops[:opsLenWithoutCleanups], DeepEquals, expected[:opsLenWithoutCleanups]) 13517 13518 // verify core in the system state 13519 var snaps map[string]*snapstate.SnapState 13520 err = s.state.Get("snaps", &snaps) 13521 c.Assert(err, IsNil) 13522 13523 snapst := snaps["core"] 13524 c.Assert(snapst, NotNil) 13525 c.Assert(snapst.Active, Equals, true) 13526 c.Assert(snapst.Channel, Equals, "stable") 13527 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 13528 RealName: "core", 13529 Channel: "stable", 13530 SnapID: "core-id", 13531 Revision: snap.R(11), 13532 }) 13533 } 13534 13535 func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsRunThrough(c *C) { 13536 s.state.Lock() 13537 defer s.state.Unlock() 13538 13539 restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond) 13540 defer restore() 13541 13542 // pretend we don't have core 13543 snapstate.Set(s.state, "core", nil) 13544 13545 chg1 := s.state.NewChange("install", "install snap 1") 13546 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 13547 ts1, err := snapstate.Install(context.Background(), s.state, "snap1", opts, s.user.ID, snapstate.Flags{}) 13548 c.Assert(err, IsNil) 13549 chg1.AddAll(ts1) 13550 13551 chg2 := s.state.NewChange("install", "install snap 2") 13552 opts = &snapstate.RevisionOptions{Channel: "some-other-channel", Revision: snap.R(21)} 13553 ts2, err := snapstate.Install(context.Background(), s.state, "snap2", opts, s.user.ID, snapstate.Flags{}) 13554 c.Assert(err, IsNil) 13555 chg2.AddAll(ts2) 13556 13557 s.state.Unlock() 13558 defer s.se.Stop() 13559 s.settle(c) 13560 s.state.Lock() 13561 13562 // ensure all our tasks ran and core was only installed once 13563 c.Assert(chg1.Err(), IsNil) 13564 c.Assert(chg2.Err(), IsNil) 13565 13566 c.Assert(chg1.IsReady(), Equals, true) 13567 c.Assert(chg2.IsReady(), Equals, true) 13568 13569 // order in which the changes run is random 13570 if len(chg1.Tasks()) < len(chg2.Tasks()) { 13571 chg1, chg2 = chg2, chg1 13572 } 13573 c.Assert(taskKinds(chg1.Tasks()), HasLen, 28) 13574 c.Assert(taskKinds(chg2.Tasks()), HasLen, 14) 13575 13576 // FIXME: add helpers and do a DeepEquals here for the operations 13577 } 13578 13579 func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsWithFailureRunThrough(c *C) { 13580 s.state.Lock() 13581 defer s.state.Unlock() 13582 13583 // slightly longer retry timeout to avoid deadlock when we 13584 // trigger a retry quickly that the link snap for core does 13585 // not have a chance to run 13586 restore := snapstate.MockPrerequisitesRetryTimeout(40 * time.Millisecond) 13587 defer restore() 13588 13589 defer s.se.Stop() 13590 // Two changes are created, the first will fails, the second will 13591 // be fine. The order of what change runs first is random, the 13592 // first change will also install core in its own lane. This test 13593 // ensures that core gets installed and there are no conflicts 13594 // even if core already got installed from the first change. 13595 // 13596 // It runs multiple times so that both possible cases get a chance 13597 // to run 13598 for i := 0; i < 5; i++ { 13599 // start clean 13600 snapstate.Set(s.state, "core", nil) 13601 snapstate.Set(s.state, "snap2", nil) 13602 13603 // chg1 has an error 13604 chg1 := s.state.NewChange("install", "install snap 1") 13605 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 13606 ts1, err := snapstate.Install(context.Background(), s.state, "snap1", opts, s.user.ID, snapstate.Flags{}) 13607 c.Assert(err, IsNil) 13608 chg1.AddAll(ts1) 13609 13610 tasks := ts1.Tasks() 13611 last := tasks[len(tasks)-1] 13612 terr := s.state.NewTask("error-trigger", "provoking total undo") 13613 terr.WaitFor(last) 13614 chg1.AddTask(terr) 13615 13616 // chg2 is good 13617 chg2 := s.state.NewChange("install", "install snap 2") 13618 opts = &snapstate.RevisionOptions{Channel: "some-other-channel", Revision: snap.R(21)} 13619 ts2, err := snapstate.Install(context.Background(), s.state, "snap2", opts, s.user.ID, snapstate.Flags{}) 13620 c.Assert(err, IsNil) 13621 chg2.AddAll(ts2) 13622 13623 // we use our own settle as we need a bigger timeout 13624 s.state.Unlock() 13625 err = s.o.Settle(15 * time.Second) 13626 s.state.Lock() 13627 c.Assert(err, IsNil) 13628 13629 // ensure expected change states 13630 c.Check(chg1.Status(), Equals, state.ErrorStatus) 13631 c.Check(chg2.Status(), Equals, state.DoneStatus) 13632 13633 // ensure we have both core and snap2 13634 var snapst snapstate.SnapState 13635 err = snapstate.Get(s.state, "core", &snapst) 13636 c.Assert(err, IsNil) 13637 c.Assert(snapst.Active, Equals, true) 13638 c.Assert(snapst.Sequence, HasLen, 1) 13639 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 13640 RealName: "core", 13641 SnapID: "core-id", 13642 Channel: "stable", 13643 Revision: snap.R(11), 13644 }) 13645 13646 var snapst2 snapstate.SnapState 13647 err = snapstate.Get(s.state, "snap2", &snapst2) 13648 c.Assert(err, IsNil) 13649 c.Assert(snapst2.Active, Equals, true) 13650 c.Assert(snapst2.Sequence, HasLen, 1) 13651 c.Assert(snapst2.Sequence[0], DeepEquals, &snap.SideInfo{ 13652 RealName: "snap2", 13653 SnapID: "snap2-id", 13654 Channel: "", 13655 Revision: snap.R(21), 13656 }) 13657 13658 } 13659 } 13660 13661 type behindYourBackStore struct { 13662 *fakeStore 13663 state *state.State 13664 13665 coreInstallRequested bool 13666 coreInstalled bool 13667 chg *state.Change 13668 } 13669 13670 func (s behindYourBackStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) { 13671 if len(actions) == 1 && actions[0].Action == "install" && actions[0].InstanceName == "core" { 13672 s.state.Lock() 13673 if !s.coreInstallRequested { 13674 s.coreInstallRequested = true 13675 snapsup := &snapstate.SnapSetup{ 13676 SideInfo: &snap.SideInfo{ 13677 RealName: "core", 13678 }, 13679 } 13680 t := s.state.NewTask("prepare", "prepare core") 13681 t.Set("snap-setup", snapsup) 13682 s.chg = s.state.NewChange("install", "install core") 13683 s.chg.AddAll(state.NewTaskSet(t)) 13684 } 13685 if s.chg != nil && !s.coreInstalled { 13686 // marks change ready but also 13687 // tasks need to also be marked cleaned 13688 for _, t := range s.chg.Tasks() { 13689 t.SetStatus(state.DoneStatus) 13690 t.SetClean() 13691 } 13692 snapstate.Set(s.state, "core", &snapstate.SnapState{ 13693 Active: true, 13694 Sequence: []*snap.SideInfo{ 13695 {RealName: "core", Revision: snap.R(1)}, 13696 }, 13697 Current: snap.R(1), 13698 SnapType: "os", 13699 }) 13700 s.coreInstalled = true 13701 } 13702 s.state.Unlock() 13703 } 13704 13705 return s.fakeStore.SnapAction(ctx, currentSnaps, actions, user, opts) 13706 } 13707 13708 // this test the scenario that some-snap gets installed and during the 13709 // install (when unlocking for the store info call for core) an 13710 // explicit "snap install core" happens. In this case the snapstate 13711 // will return a change conflict. we handle this via a retry, ensure 13712 // this is actually what happens. 13713 func (s *snapmgrTestSuite) TestInstallWithoutCoreConflictingInstall(c *C) { 13714 s.state.Lock() 13715 defer s.state.Unlock() 13716 13717 restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond) 13718 defer restore() 13719 13720 snapstate.ReplaceStore(s.state, behindYourBackStore{fakeStore: s.fakeStore, state: s.state}) 13721 13722 // pretend we don't have core 13723 snapstate.Set(s.state, "core", nil) 13724 13725 // now install a snap that will pull in core 13726 chg := s.state.NewChange("install", "install a snap on a system without core") 13727 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 13728 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 13729 c.Assert(err, IsNil) 13730 chg.AddAll(ts) 13731 13732 prereq := ts.Tasks()[0] 13733 c.Assert(prereq.Kind(), Equals, "prerequisites") 13734 c.Check(prereq.AtTime().IsZero(), Equals, true) 13735 13736 s.state.Unlock() 13737 defer s.se.Stop() 13738 13739 // start running the change, this will trigger the 13740 // prerequisites task, which will trigger the install of core 13741 // and also call our mock store which will generate a parallel 13742 // change 13743 s.se.Ensure() 13744 s.se.Wait() 13745 13746 // change is not ready yet, because the prerequists triggered 13747 // a state.Retry{} because of the conflicting change 13748 c.Assert(chg.IsReady(), Equals, false) 13749 s.state.Lock() 13750 // marked for retry 13751 c.Check(prereq.AtTime().IsZero(), Equals, false) 13752 c.Check(prereq.Status().Ready(), Equals, false) 13753 s.state.Unlock() 13754 13755 // retry interval is 10ms so 20ms should be plenty of time 13756 time.Sleep(20 * time.Millisecond) 13757 s.settle(c) 13758 // chg got retried, core is now installed, things are good 13759 c.Assert(chg.IsReady(), Equals, true) 13760 13761 s.state.Lock() 13762 13763 // ensure all our tasks ran 13764 c.Assert(chg.Err(), IsNil) 13765 c.Assert(chg.IsReady(), Equals, true) 13766 13767 // verify core in the system state 13768 var snaps map[string]*snapstate.SnapState 13769 err = s.state.Get("snaps", &snaps) 13770 c.Assert(err, IsNil) 13771 13772 snapst := snaps["core"] 13773 c.Assert(snapst, NotNil) 13774 c.Assert(snapst.Active, Equals, true) 13775 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 13776 RealName: "core", 13777 Revision: snap.R(1), 13778 }) 13779 13780 snapst = snaps["some-snap"] 13781 c.Assert(snapst, NotNil) 13782 c.Assert(snapst.Active, Equals, true) 13783 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 13784 RealName: "some-snap", 13785 SnapID: "some-snap-id", 13786 Channel: "some-channel", 13787 Revision: snap.R(11), 13788 }) 13789 } 13790 13791 type contentStore struct { 13792 *fakeStore 13793 state *state.State 13794 } 13795 13796 func (s contentStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) { 13797 snaps, err := s.fakeStore.SnapAction(ctx, currentSnaps, actions, user, opts) 13798 if err != nil { 13799 return nil, err 13800 } 13801 if len(snaps) != 1 { 13802 panic("expected to be queried for install of only one snap at a time") 13803 } 13804 info := snaps[0] 13805 switch info.InstanceName() { 13806 case "snap-content-plug": 13807 info.Plugs = map[string]*snap.PlugInfo{ 13808 "some-plug": { 13809 Snap: info, 13810 Name: "shared-content", 13811 Interface: "content", 13812 Attrs: map[string]interface{}{ 13813 "default-provider": "snap-content-slot", 13814 "content": "shared-content", 13815 }, 13816 }, 13817 } 13818 case "snap-content-plug-compat": 13819 info.Plugs = map[string]*snap.PlugInfo{ 13820 "some-plug": { 13821 Snap: info, 13822 Name: "shared-content", 13823 Interface: "content", 13824 Attrs: map[string]interface{}{ 13825 "default-provider": "snap-content-slot:some-slot", 13826 "content": "shared-content", 13827 }, 13828 }, 13829 } 13830 case "snap-content-slot": 13831 info.Slots = map[string]*snap.SlotInfo{ 13832 "some-slot": { 13833 Snap: info, 13834 Name: "shared-content", 13835 Interface: "content", 13836 Attrs: map[string]interface{}{ 13837 "content": "shared-content", 13838 }, 13839 }, 13840 } 13841 case "snap-content-circular1": 13842 info.Plugs = map[string]*snap.PlugInfo{ 13843 "circular-plug1": { 13844 Snap: info, 13845 Name: "circular-plug1", 13846 Interface: "content", 13847 Attrs: map[string]interface{}{ 13848 "default-provider": "snap-content-circular2", 13849 "content": "circular2", 13850 }, 13851 }, 13852 } 13853 info.Slots = map[string]*snap.SlotInfo{ 13854 "circular-slot1": { 13855 Snap: info, 13856 Name: "circular-slot1", 13857 Interface: "content", 13858 Attrs: map[string]interface{}{ 13859 "content": "circular1", 13860 }, 13861 }, 13862 } 13863 case "snap-content-circular2": 13864 info.Plugs = map[string]*snap.PlugInfo{ 13865 "circular-plug2": { 13866 Snap: info, 13867 Name: "circular-plug2", 13868 Interface: "content", 13869 Attrs: map[string]interface{}{ 13870 "default-provider": "snap-content-circular1", 13871 "content": "circular2", 13872 }, 13873 }, 13874 } 13875 info.Slots = map[string]*snap.SlotInfo{ 13876 "circular-slot2": { 13877 Snap: info, 13878 Name: "circular-slot2", 13879 Interface: "content", 13880 Attrs: map[string]interface{}{ 13881 "content": "circular1", 13882 }, 13883 }, 13884 } 13885 } 13886 13887 return []*snap.Info{info}, err 13888 } 13889 13890 func (s *snapmgrTestSuite) TestInstallDefaultProviderRunThrough(c *C) { 13891 s.state.Lock() 13892 defer s.state.Unlock() 13893 13894 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 13895 13896 repo := interfaces.NewRepository() 13897 ifacerepo.Replace(s.state, repo) 13898 13899 chg := s.state.NewChange("install", "install a snap") 13900 opts := &snapstate.RevisionOptions{Channel: "stable", Revision: snap.R(42)} 13901 ts, err := snapstate.Install(context.Background(), s.state, "snap-content-plug", opts, s.user.ID, snapstate.Flags{}) 13902 c.Assert(err, IsNil) 13903 chg.AddAll(ts) 13904 13905 s.state.Unlock() 13906 defer s.se.Stop() 13907 s.settle(c) 13908 s.state.Lock() 13909 13910 // ensure all our tasks ran 13911 c.Assert(chg.Err(), IsNil) 13912 c.Assert(chg.IsReady(), Equals, true) 13913 expected := fakeOps{{ 13914 op: "storesvc-snap-action", 13915 userID: 1, 13916 }, { 13917 op: "storesvc-snap-action:action", 13918 action: store.SnapAction{ 13919 Action: "install", 13920 InstanceName: "snap-content-plug", 13921 Revision: snap.R(42), 13922 }, 13923 revno: snap.R(42), 13924 userID: 1, 13925 }, { 13926 op: "storesvc-snap-action", 13927 userID: 1, 13928 }, { 13929 op: "storesvc-snap-action:action", 13930 action: store.SnapAction{ 13931 Action: "install", 13932 InstanceName: "snap-content-slot", 13933 Channel: "stable", 13934 }, 13935 revno: snap.R(11), 13936 userID: 1, 13937 }, { 13938 op: "storesvc-download", 13939 name: "snap-content-slot", 13940 }, { 13941 op: "validate-snap:Doing", 13942 name: "snap-content-slot", 13943 revno: snap.R(11), 13944 }, { 13945 op: "current", 13946 old: "<no-current>", 13947 }, { 13948 op: "open-snap-file", 13949 path: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap"), 13950 sinfo: snap.SideInfo{ 13951 RealName: "snap-content-slot", 13952 Channel: "stable", 13953 SnapID: "snap-content-slot-id", 13954 Revision: snap.R(11), 13955 }, 13956 }, { 13957 op: "setup-snap", 13958 name: "snap-content-slot", 13959 path: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap"), 13960 revno: snap.R(11), 13961 }, { 13962 op: "copy-data", 13963 path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"), 13964 old: "<no-old>", 13965 }, { 13966 op: "setup-profiles:Doing", 13967 name: "snap-content-slot", 13968 revno: snap.R(11), 13969 }, { 13970 op: "candidate", 13971 sinfo: snap.SideInfo{ 13972 RealName: "snap-content-slot", 13973 Channel: "stable", 13974 SnapID: "snap-content-slot-id", 13975 Revision: snap.R(11), 13976 }, 13977 }, { 13978 op: "link-snap", 13979 path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"), 13980 }, { 13981 op: "auto-connect:Doing", 13982 name: "snap-content-slot", 13983 revno: snap.R(11), 13984 }, { 13985 op: "update-aliases", 13986 }, { 13987 op: "storesvc-download", 13988 name: "snap-content-plug", 13989 }, { 13990 op: "validate-snap:Doing", 13991 name: "snap-content-plug", 13992 revno: snap.R(42), 13993 }, { 13994 op: "current", 13995 old: "<no-current>", 13996 }, { 13997 op: "open-snap-file", 13998 path: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_42.snap"), 13999 sinfo: snap.SideInfo{ 14000 RealName: "snap-content-plug", 14001 SnapID: "snap-content-plug-id", 14002 Revision: snap.R(42), 14003 }, 14004 }, { 14005 op: "setup-snap", 14006 name: "snap-content-plug", 14007 path: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_42.snap"), 14008 revno: snap.R(42), 14009 }, { 14010 op: "copy-data", 14011 path: filepath.Join(dirs.SnapMountDir, "snap-content-plug/42"), 14012 old: "<no-old>", 14013 }, { 14014 op: "setup-profiles:Doing", 14015 name: "snap-content-plug", 14016 revno: snap.R(42), 14017 }, { 14018 op: "candidate", 14019 sinfo: snap.SideInfo{ 14020 RealName: "snap-content-plug", 14021 SnapID: "snap-content-plug-id", 14022 Revision: snap.R(42), 14023 }, 14024 }, { 14025 op: "link-snap", 14026 path: filepath.Join(dirs.SnapMountDir, "snap-content-plug/42"), 14027 }, { 14028 op: "auto-connect:Doing", 14029 name: "snap-content-plug", 14030 revno: snap.R(42), 14031 }, { 14032 op: "update-aliases", 14033 }, { 14034 op: "cleanup-trash", 14035 name: "snap-content-plug", 14036 revno: snap.R(42), 14037 }, { 14038 op: "cleanup-trash", 14039 name: "snap-content-slot", 14040 revno: snap.R(11), 14041 }, 14042 } 14043 // snap and default provider are installed in parallel so we can't 14044 // do a simple c.Check(ops, DeepEquals, fakeOps{...}) 14045 c.Check(len(s.fakeBackend.ops), Equals, len(expected)) 14046 for _, op := range expected { 14047 c.Assert(s.fakeBackend.ops, testutil.DeepContains, op) 14048 } 14049 for _, op := range s.fakeBackend.ops { 14050 c.Assert(expected, testutil.DeepContains, op) 14051 } 14052 } 14053 14054 func (s *snapmgrTestSuite) TestInstallDefaultProviderCircular(c *C) { 14055 s.state.Lock() 14056 defer s.state.Unlock() 14057 14058 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 14059 14060 repo := interfaces.NewRepository() 14061 ifacerepo.Replace(s.state, repo) 14062 14063 chg := s.state.NewChange("install", "install a snap") 14064 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 14065 ts, err := snapstate.Install(context.Background(), s.state, "snap-content-circular1", opts, s.user.ID, snapstate.Flags{}) 14066 c.Assert(err, IsNil) 14067 chg.AddAll(ts) 14068 14069 s.state.Unlock() 14070 defer s.se.Stop() 14071 s.settle(c) 14072 s.state.Lock() 14073 14074 // ensure all our tasks ran 14075 c.Assert(chg.Err(), IsNil) 14076 c.Assert(chg.IsReady(), Equals, true) 14077 // and both circular snaps got linked 14078 c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{ 14079 op: "link-snap", 14080 path: filepath.Join(dirs.SnapMountDir, "snap-content-circular1/42"), 14081 }) 14082 c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{ 14083 op: "link-snap", 14084 path: filepath.Join(dirs.SnapMountDir, "snap-content-circular2/11"), 14085 }) 14086 } 14087 14088 func (s *snapmgrTestSuite) TestInstallDefaultProviderCompat(c *C) { 14089 s.state.Lock() 14090 defer s.state.Unlock() 14091 14092 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 14093 14094 repo := interfaces.NewRepository() 14095 ifacerepo.Replace(s.state, repo) 14096 14097 chg := s.state.NewChange("install", "install a snap") 14098 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 14099 ts, err := snapstate.Install(context.Background(), s.state, "snap-content-plug-compat", opts, s.user.ID, snapstate.Flags{}) 14100 c.Assert(err, IsNil) 14101 chg.AddAll(ts) 14102 14103 s.state.Unlock() 14104 defer s.se.Stop() 14105 s.settle(c) 14106 s.state.Lock() 14107 14108 // ensure all our tasks ran 14109 c.Assert(chg.Err(), IsNil) 14110 c.Assert(chg.IsReady(), Equals, true) 14111 // and both circular snaps got linked 14112 c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{ 14113 op: "link-snap", 14114 path: filepath.Join(dirs.SnapMountDir, "snap-content-plug-compat/42"), 14115 }) 14116 c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{ 14117 op: "link-snap", 14118 path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"), 14119 }) 14120 } 14121 14122 func (s *snapmgrTestSuite) TestSnapManagerLegacyRefreshSchedule(c *C) { 14123 s.state.Lock() 14124 defer s.state.Unlock() 14125 14126 for _, t := range []struct { 14127 in string 14128 out string 14129 legacy bool 14130 }{ 14131 {"", snapstate.DefaultRefreshSchedule, false}, 14132 {"invalid schedule", snapstate.DefaultRefreshSchedule, false}, 14133 {"8:00-12:00", "8:00-12:00", true}, 14134 // using the legacy configuration option with a new-style 14135 // refresh.timer string is rejected (i.e. the legacy parser is 14136 // used for the parsing) 14137 {"0:00~24:00/24", snapstate.DefaultRefreshSchedule, false}, 14138 } { 14139 if t.in != "" { 14140 tr := config.NewTransaction(s.state) 14141 tr.Set("core", "refresh.timer", "") 14142 tr.Set("core", "refresh.schedule", t.in) 14143 tr.Commit() 14144 } 14145 scheduleStr, legacy, err := s.snapmgr.RefreshSchedule() 14146 c.Check(err, IsNil) 14147 c.Check(scheduleStr, Equals, t.out) 14148 c.Check(legacy, Equals, t.legacy) 14149 } 14150 } 14151 14152 func (s *snapmgrTestSuite) TestSnapManagerRefreshSchedule(c *C) { 14153 s.state.Lock() 14154 defer s.state.Unlock() 14155 14156 for _, t := range []struct { 14157 in string 14158 out string 14159 }{ 14160 {"", snapstate.DefaultRefreshSchedule}, 14161 {"invalid schedule", snapstate.DefaultRefreshSchedule}, 14162 {"8:00-12:00", "8:00-12:00"}, 14163 // this is only valid under the new schedule parser 14164 {"9:00~15:00/2,,mon,20:00", "9:00~15:00/2,,mon,20:00"}, 14165 } { 14166 if t.in != "" { 14167 tr := config.NewTransaction(s.state) 14168 tr.Set("core", "refresh.timer", t.in) 14169 tr.Commit() 14170 } 14171 scheduleStr, legacy, err := s.snapmgr.RefreshSchedule() 14172 c.Check(err, IsNil) 14173 c.Check(scheduleStr, Equals, t.out) 14174 c.Check(legacy, Equals, false) 14175 } 14176 } 14177 14178 func (s *snapmgrTestSuite) TestSideInfoPaid(c *C) { 14179 s.state.Lock() 14180 defer s.state.Unlock() 14181 opts := &snapstate.RevisionOptions{Channel: "channel-for-paid"} 14182 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 14183 c.Assert(err, IsNil) 14184 14185 chg := s.state.NewChange("install", "install paid snap") 14186 chg.AddAll(ts) 14187 14188 s.state.Unlock() 14189 defer s.se.Stop() 14190 s.settle(c) 14191 s.state.Lock() 14192 14193 // verify snap has paid sideinfo 14194 var snapst snapstate.SnapState 14195 err = snapstate.Get(s.state, "some-snap", &snapst) 14196 c.Assert(err, IsNil) 14197 c.Check(snapst.CurrentSideInfo().Paid, Equals, true) 14198 c.Check(snapst.CurrentSideInfo().Private, Equals, false) 14199 } 14200 14201 func (s *snapmgrTestSuite) TestSideInfoPrivate(c *C) { 14202 s.state.Lock() 14203 defer s.state.Unlock() 14204 opts := &snapstate.RevisionOptions{Channel: "channel-for-private"} 14205 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 14206 c.Assert(err, IsNil) 14207 14208 chg := s.state.NewChange("install", "install private snap") 14209 chg.AddAll(ts) 14210 14211 s.state.Unlock() 14212 defer s.se.Stop() 14213 s.settle(c) 14214 s.state.Lock() 14215 14216 // verify snap has private sideinfo 14217 var snapst snapstate.SnapState 14218 err = snapstate.Get(s.state, "some-snap", &snapst) 14219 c.Assert(err, IsNil) 14220 c.Check(snapst.CurrentSideInfo().Private, Equals, true) 14221 c.Check(snapst.CurrentSideInfo().Paid, Equals, false) 14222 } 14223 14224 func (s *snapmgrTestSuite) TestInstallPathWithLayoutsChecksFeatureFlag(c *C) { 14225 s.state.Lock() 14226 defer s.state.Unlock() 14227 14228 // When layouts are disabled we cannot install a local snap depending on the feature. 14229 tr := config.NewTransaction(s.state) 14230 tr.Set("core", "experimental.layouts", false) 14231 tr.Commit() 14232 14233 mockSnap := makeTestSnap(c, `name: some-snap 14234 version: 1.0 14235 layout: 14236 /usr: 14237 bind: $SNAP/usr 14238 `) 14239 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{}) 14240 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true") 14241 14242 // When layouts are enabled we can install a local snap depending on the feature. 14243 tr = config.NewTransaction(s.state) 14244 tr.Set("core", "experimental.layouts", true) 14245 tr.Commit() 14246 14247 _, _, err = snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{}) 14248 c.Assert(err, IsNil) 14249 } 14250 14251 func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchKernel(c *C) { 14252 // use the real thing for this one 14253 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 14254 14255 s.state.Lock() 14256 defer s.state.Unlock() 14257 14258 // snapd cannot be installed unless the model uses a base snap 14259 r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 14260 defer r() 14261 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 14262 Sequence: []*snap.SideInfo{ 14263 {RealName: "kernel", Revision: snap.R(11)}, 14264 }, 14265 Channel: "18/stable", 14266 Current: snap.R(11), 14267 Active: true, 14268 }) 14269 14270 someSnap := makeTestSnap(c, `name: kernel 14271 version: 1.0`) 14272 si := &snap.SideInfo{ 14273 RealName: "kernel", 14274 SnapID: "kernel-id", 14275 Revision: snap.R(42), 14276 Channel: "some-channel", 14277 } 14278 _, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true}) 14279 c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "some-channel"`) 14280 } 14281 14282 func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchGadget(c *C) { 14283 // use the real thing for this one 14284 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 14285 14286 s.state.Lock() 14287 defer s.state.Unlock() 14288 14289 // snapd cannot be installed unless the model uses a base snap 14290 r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18")) 14291 defer r() 14292 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 14293 Sequence: []*snap.SideInfo{ 14294 {RealName: "brand-gadget", Revision: snap.R(11)}, 14295 }, 14296 Channel: "18/stable", 14297 Current: snap.R(11), 14298 Active: true, 14299 }) 14300 14301 someSnap := makeTestSnap(c, `name: brand-gadget 14302 version: 1.0`) 14303 si := &snap.SideInfo{ 14304 RealName: "brand-gadget", 14305 SnapID: "brand-gadget-id", 14306 Revision: snap.R(42), 14307 Channel: "some-channel", 14308 } 14309 _, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true}) 14310 c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "some-channel"`) 14311 } 14312 14313 func (s *snapmgrTestSuite) TestInstallLayoutsChecksFeatureFlag(c *C) { 14314 s.state.Lock() 14315 defer s.state.Unlock() 14316 14317 // Layouts are now enabled by default. 14318 opts := &snapstate.RevisionOptions{Channel: "channel-for-layout"} 14319 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 14320 c.Assert(err, IsNil) 14321 14322 // Layouts can be explicitly disabled. 14323 tr := config.NewTransaction(s.state) 14324 tr.Set("core", "experimental.layouts", false) 14325 tr.Commit() 14326 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 14327 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true") 14328 14329 // Layouts can be explicitly enabled. 14330 tr = config.NewTransaction(s.state) 14331 tr.Set("core", "experimental.layouts", true) 14332 tr.Commit() 14333 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 14334 c.Assert(err, IsNil) 14335 14336 // The default empty value now means "enabled". 14337 tr = config.NewTransaction(s.state) 14338 tr.Set("core", "experimental.layouts", "") 14339 tr.Commit() 14340 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 14341 c.Assert(err, IsNil) 14342 14343 // Layouts are enabled when the controlling flag is reset to nil. 14344 tr = config.NewTransaction(s.state) 14345 tr.Set("core", "experimental.layouts", nil) 14346 tr.Commit() 14347 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 14348 c.Assert(err, IsNil) 14349 14350 } 14351 14352 func (s *snapmgrTestSuite) TestUpdateLayoutsChecksFeatureFlag(c *C) { 14353 s.state.Lock() 14354 defer s.state.Unlock() 14355 14356 // When layouts are disabled we cannot refresh to a snap depending on the feature. 14357 tr := config.NewTransaction(s.state) 14358 tr.Set("core", "experimental.layouts", false) 14359 tr.Commit() 14360 14361 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 14362 Active: true, 14363 Sequence: []*snap.SideInfo{ 14364 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 14365 }, 14366 Current: snap.R(1), 14367 SnapType: "app", 14368 }) 14369 14370 _, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-layout"}, s.user.ID, snapstate.Flags{}) 14371 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true") 14372 14373 // When layouts are enabled we can refresh to a snap depending on the feature. 14374 tr = config.NewTransaction(s.state) 14375 tr.Set("core", "experimental.layouts", true) 14376 tr.Commit() 14377 14378 _, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-layout"}, s.user.ID, snapstate.Flags{}) 14379 c.Assert(err, IsNil) 14380 } 14381 14382 func (s *snapmgrTestSuite) TestUpdateManyExplicitLayoutsChecksFeatureFlag(c *C) { 14383 s.state.Lock() 14384 defer s.state.Unlock() 14385 14386 // When layouts are disabled we cannot refresh multiple snaps if one of them depends on the feature. 14387 tr := config.NewTransaction(s.state) 14388 tr.Set("core", "experimental.layouts", false) 14389 tr.Commit() 14390 14391 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 14392 Active: true, 14393 Channel: "channel-for-layout", 14394 Sequence: []*snap.SideInfo{ 14395 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 14396 }, 14397 Current: snap.R(1), 14398 SnapType: "app", 14399 }) 14400 14401 _, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil) 14402 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true") 14403 14404 // When layouts are enabled we can refresh multiple snaps if one of them depends on the feature. 14405 tr = config.NewTransaction(s.state) 14406 tr.Set("core", "experimental.layouts", true) 14407 tr.Commit() 14408 14409 _, _, err = snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil) 14410 c.Assert(err, IsNil) 14411 } 14412 14413 func (s *snapmgrTestSuite) TestUpdateManyLayoutsChecksFeatureFlag(c *C) { 14414 s.state.Lock() 14415 defer s.state.Unlock() 14416 14417 // When layouts are disabled we cannot refresh multiple snaps if one of them depends on the feature. 14418 tr := config.NewTransaction(s.state) 14419 tr.Set("core", "experimental.layouts", false) 14420 tr.Commit() 14421 14422 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 14423 Active: true, 14424 Channel: "channel-for-layout", 14425 Sequence: []*snap.SideInfo{ 14426 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 14427 }, 14428 Current: snap.R(1), 14429 SnapType: "app", 14430 }) 14431 14432 refreshes, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil) 14433 c.Assert(err, IsNil) 14434 c.Assert(refreshes, HasLen, 0) 14435 14436 // When layouts are enabled we can refresh multiple snaps if one of them depends on the feature. 14437 tr = config.NewTransaction(s.state) 14438 tr.Set("core", "experimental.layouts", true) 14439 tr.Commit() 14440 14441 refreshes, _, err = snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil) 14442 c.Assert(err, IsNil) 14443 c.Assert(refreshes, DeepEquals, []string{"some-snap"}) 14444 } 14445 14446 func (s *snapmgrTestSuite) TestUpdateFailsEarlyOnEpochMismatch(c *C) { 14447 s.state.Lock() 14448 defer s.state.Unlock() 14449 14450 snapstate.Set(s.state, "some-epoch-snap", &snapstate.SnapState{ 14451 Active: true, 14452 Sequence: []*snap.SideInfo{ 14453 {RealName: "some-epoch-snap", SnapID: "some-epoch-snap-id", Revision: snap.R(1)}, 14454 }, 14455 Current: snap.R(1), 14456 SnapType: "app", 14457 }) 14458 14459 _, err := snapstate.Update(s.state, "some-epoch-snap", nil, 0, snapstate.Flags{}) 14460 c.Assert(err, ErrorMatches, `cannot refresh "some-epoch-snap" to new revision 11 with epoch 42, because it can't read the current epoch of 13`) 14461 } 14462 14463 func (s *snapmgrTestSuite) TestParallelInstallValidateFeatureFlag(c *C) { 14464 s.state.Lock() 14465 defer s.state.Unlock() 14466 14467 info := &snap.Info{ 14468 InstanceKey: "foo", 14469 } 14470 14471 err := snapstate.ValidateFeatureFlags(s.state, info) 14472 c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`) 14473 14474 // various forms of disabling 14475 tr := config.NewTransaction(s.state) 14476 tr.Set("core", "experimental.parallel-instances", false) 14477 tr.Commit() 14478 14479 err = snapstate.ValidateFeatureFlags(s.state, info) 14480 c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`) 14481 14482 tr = config.NewTransaction(s.state) 14483 tr.Set("core", "experimental.parallel-instances", "") 14484 tr.Commit() 14485 14486 err = snapstate.ValidateFeatureFlags(s.state, info) 14487 c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`) 14488 14489 tr = config.NewTransaction(s.state) 14490 tr.Set("core", "experimental.parallel-instances", nil) 14491 tr.Commit() 14492 14493 err = snapstate.ValidateFeatureFlags(s.state, info) 14494 c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`) 14495 14496 tr = config.NewTransaction(s.state) 14497 tr.Set("core", "experimental.parallel-instances", "veryfalse") 14498 tr.Commit() 14499 14500 err = snapstate.ValidateFeatureFlags(s.state, info) 14501 c.Assert(err, ErrorMatches, `parallel-instances can only be set to 'true' or 'false', got "veryfalse"`) 14502 14503 // enable parallel instances 14504 tr = config.NewTransaction(s.state) 14505 tr.Set("core", "experimental.parallel-instances", true) 14506 tr.Commit() 14507 14508 err = snapstate.ValidateFeatureFlags(s.state, info) 14509 c.Assert(err, IsNil) 14510 } 14511 14512 func (s *snapmgrTestSuite) TestParallelInstallInstallPathExperimentalSwitch(c *C) { 14513 s.state.Lock() 14514 defer s.state.Unlock() 14515 14516 mockSnap := makeTestSnap(c, `name: some-snap 14517 version: 1.0 14518 `) 14519 si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)} 14520 _, _, err := snapstate.InstallPath(s.state, si, mockSnap, "some-snap_foo", "", snapstate.Flags{}) 14521 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.parallel-instances' to true") 14522 14523 // enable parallel instances 14524 tr := config.NewTransaction(s.state) 14525 tr.Set("core", "experimental.parallel-instances", true) 14526 tr.Commit() 14527 14528 _, _, err = snapstate.InstallPath(s.state, si, mockSnap, "some-snap_foo", "", snapstate.Flags{}) 14529 c.Assert(err, IsNil) 14530 } 14531 14532 func (s *snapmgrTestSuite) TestInjectTasks(c *C) { 14533 s.state.Lock() 14534 defer s.state.Unlock() 14535 14536 lane := s.state.NewLane() 14537 14538 // setup main task and two tasks waiting for it; all part of same change 14539 chg := s.state.NewChange("change", "") 14540 t0 := s.state.NewTask("task1", "") 14541 chg.AddTask(t0) 14542 t0.JoinLane(lane) 14543 t01 := s.state.NewTask("task1-1", "") 14544 t01.WaitFor(t0) 14545 chg.AddTask(t01) 14546 t02 := s.state.NewTask("task1-2", "") 14547 t02.WaitFor(t0) 14548 chg.AddTask(t02) 14549 14550 // setup extra tasks 14551 t1 := s.state.NewTask("task2", "") 14552 t2 := s.state.NewTask("task3", "") 14553 ts := state.NewTaskSet(t1, t2) 14554 14555 snapstate.InjectTasks(t0, ts) 14556 14557 // verify that extra tasks are now part of same change 14558 c.Assert(t1.Change().ID(), Equals, t0.Change().ID()) 14559 c.Assert(t2.Change().ID(), Equals, t0.Change().ID()) 14560 c.Assert(t1.Change().ID(), Equals, chg.ID()) 14561 14562 c.Assert(t1.Lanes(), DeepEquals, []int{lane}) 14563 14564 // verify that halt tasks of the main task now wait for extra tasks 14565 c.Assert(t1.HaltTasks(), HasLen, 2) 14566 c.Assert(t2.HaltTasks(), HasLen, 2) 14567 c.Assert(t1.HaltTasks(), DeepEquals, t2.HaltTasks()) 14568 14569 ids := []string{t1.HaltTasks()[0].Kind(), t2.HaltTasks()[1].Kind()} 14570 sort.Strings(ids) 14571 c.Assert(ids, DeepEquals, []string{"task1-1", "task1-2"}) 14572 14573 // verify that extra tasks wait for the main task 14574 c.Assert(t1.WaitTasks(), HasLen, 1) 14575 c.Assert(t1.WaitTasks()[0].Kind(), Equals, "task1") 14576 c.Assert(t2.WaitTasks(), HasLen, 1) 14577 c.Assert(t2.WaitTasks()[0].Kind(), Equals, "task1") 14578 } 14579 14580 func (s *snapmgrTestSuite) TestInjectTasksWithNullChange(c *C) { 14581 s.state.Lock() 14582 defer s.state.Unlock() 14583 14584 // setup main task 14585 t0 := s.state.NewTask("task1", "") 14586 t01 := s.state.NewTask("task1-1", "") 14587 t01.WaitFor(t0) 14588 14589 // setup extra task 14590 t1 := s.state.NewTask("task2", "") 14591 ts := state.NewTaskSet(t1) 14592 14593 snapstate.InjectTasks(t0, ts) 14594 14595 c.Assert(t1.Lanes(), DeepEquals, []int{0}) 14596 14597 // verify that halt tasks of the main task now wait for extra tasks 14598 c.Assert(t1.HaltTasks(), HasLen, 1) 14599 c.Assert(t1.HaltTasks()[0].Kind(), Equals, "task1-1") 14600 } 14601 14602 func hasConfigureTask(ts *state.TaskSet) bool { 14603 for _, tk := range taskKinds(ts.Tasks()) { 14604 if tk == "run-hook[configure]" { 14605 return true 14606 } 14607 } 14608 return false 14609 } 14610 14611 func (s *snapmgrTestSuite) TestNoConfigureForBasesTask(c *C) { 14612 s.state.Lock() 14613 defer s.state.Unlock() 14614 14615 // normal snaps get a configure task 14616 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 14617 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 14618 c.Assert(err, IsNil) 14619 c.Check(hasConfigureTask(ts), Equals, true) 14620 14621 // but bases do not for install 14622 ts, err = snapstate.Install(context.Background(), s.state, "some-base", opts, s.user.ID, snapstate.Flags{}) 14623 c.Assert(err, IsNil) 14624 c.Check(hasConfigureTask(ts), Equals, false) 14625 14626 // or for refresh 14627 snapstate.Set(s.state, "some-base", &snapstate.SnapState{ 14628 Active: true, 14629 Channel: "edge", 14630 Sequence: []*snap.SideInfo{{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)}}, 14631 Current: snap.R(1), 14632 SnapType: "base", 14633 }) 14634 ts, err = snapstate.Update(s.state, "some-base", nil, s.user.ID, snapstate.Flags{}) 14635 c.Assert(err, IsNil) 14636 c.Check(hasConfigureTask(ts), Equals, false) 14637 } 14638 14639 func (s *snapmgrTestSuite) TestNoSnapdSnapOnCoreWithoutBase(c *C) { 14640 s.state.Lock() 14641 defer s.state.Unlock() 14642 r := release.MockOnClassic(false) 14643 defer r() 14644 14645 // but snapd do not for install 14646 _, err := snapstate.Install(context.Background(), s.state, "snapd", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 14647 c.Assert(err, ErrorMatches, "cannot install snapd snap on a model without a base snap yet") 14648 } 14649 14650 func (s *snapmgrTestSuite) TestNoSnapdSnapOnSystemsWithoutBaseOnUbuntuCore(c *C) { 14651 s.state.Lock() 14652 defer s.state.Unlock() 14653 r := release.MockOnClassic(false) 14654 defer r() 14655 14656 // it is not possible to opt-into the snapd snap on core yet 14657 tr := config.NewTransaction(s.state) 14658 tr.Set("core", "experimental.snapd-snap", true) 14659 tr.Commit() 14660 14661 // but snapd do not for install 14662 _, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{}) 14663 c.Assert(err, ErrorMatches, "cannot install snapd snap on a model without a base snap yet") 14664 } 14665 14666 func (s *snapmgrTestSuite) TestNoSnapdSnapOnSystemsWithoutBaseButOption(c *C) { 14667 s.state.Lock() 14668 defer s.state.Unlock() 14669 14670 tr := config.NewTransaction(s.state) 14671 tr.Set("core", "experimental.snapd-snap", true) 14672 tr.Commit() 14673 14674 _, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{}) 14675 c.Assert(err, IsNil) 14676 } 14677 14678 func (s *snapmgrTestSuite) TestNoConfigureForSnapdSnap(c *C) { 14679 s.state.Lock() 14680 defer s.state.Unlock() 14681 14682 restore := snap.MockSnapdSnapID("snapd-id") 14683 defer restore() 14684 14685 // snapd cannot be installed unless the model uses a base snap 14686 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 14687 defer r() 14688 14689 // but snapd do not for install 14690 ts, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{}) 14691 c.Assert(err, IsNil) 14692 c.Check(hasConfigureTask(ts), Equals, false) 14693 14694 // or for refresh 14695 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 14696 Active: true, 14697 Channel: "edge", 14698 Sequence: []*snap.SideInfo{{RealName: "snapd", SnapID: "snapd-id", Revision: snap.R(1)}}, 14699 Current: snap.R(1), 14700 SnapType: "app", 14701 }) 14702 ts, err = snapstate.Update(s.state, "snapd", nil, s.user.ID, snapstate.Flags{}) 14703 c.Assert(err, IsNil) 14704 c.Check(hasConfigureTask(ts), Equals, false) 14705 14706 } 14707 14708 func (s snapmgrTestSuite) TestCanLoadOldSnapSetupWithoutType(c *C) { 14709 // ensure we don't crash when loading a SnapSetup json without 14710 // a type set 14711 oldSnapSetup := []byte(`{ 14712 "snap-path":"/some/path", 14713 "side-info": { 14714 "channel": "edge", 14715 "name": "some-snap", 14716 "revision": "1", 14717 "snap-id": "some-snap-id" 14718 } 14719 }`) 14720 var snapsup snapstate.SnapSetup 14721 err := json.Unmarshal(oldSnapSetup, &snapsup) 14722 c.Assert(err, IsNil) 14723 c.Check(snapsup.SnapPath, Equals, "/some/path") 14724 c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 14725 Channel: "edge", 14726 RealName: "some-snap", 14727 Revision: snap.R(1), 14728 SnapID: "some-snap-id", 14729 }) 14730 c.Check(snapsup.Type, Equals, snap.Type("")) 14731 } 14732 14733 func (s snapmgrTestSuite) TestHasOtherInstances(c *C) { 14734 s.state.Lock() 14735 defer s.state.Unlock() 14736 14737 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 14738 Active: true, 14739 Sequence: []*snap.SideInfo{ 14740 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 14741 }, 14742 Current: snap.R(1), 14743 SnapType: "app", 14744 }) 14745 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 14746 Active: true, 14747 Sequence: []*snap.SideInfo{ 14748 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 14749 }, 14750 Current: snap.R(3), 14751 SnapType: "app", 14752 InstanceKey: "instance", 14753 }) 14754 snapstate.Set(s.state, "some-other-snap", &snapstate.SnapState{ 14755 Active: true, 14756 Sequence: []*snap.SideInfo{ 14757 {RealName: "some-other-snap", SnapID: "some-other-snap-id", Revision: snap.R(1)}, 14758 }, 14759 Current: snap.R(1), 14760 SnapType: "app", 14761 }) 14762 14763 other, err := snapstate.HasOtherInstances(s.state, "some-snap") 14764 c.Assert(err, IsNil) 14765 c.Assert(other, Equals, true) 14766 other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance") 14767 c.Assert(err, IsNil) 14768 c.Assert(other, Equals, true) 14769 other, err = snapstate.HasOtherInstances(s.state, "some-other-snap") 14770 c.Assert(err, IsNil) 14771 c.Assert(other, Equals, false) 14772 // other snaps like only looks at the name of the refence snap 14773 other, err = snapstate.HasOtherInstances(s.state, "some-other-snap_instance") 14774 c.Assert(err, IsNil) 14775 c.Assert(other, Equals, true) 14776 14777 // remove the snap without instance key 14778 snapstate.Set(s.state, "some-snap", nil) 14779 // some-snap_instance is like some-snap 14780 other, err = snapstate.HasOtherInstances(s.state, "some-snap") 14781 c.Assert(err, IsNil) 14782 c.Assert(other, Equals, true) 14783 other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance") 14784 c.Assert(err, IsNil) 14785 c.Assert(other, Equals, false) 14786 14787 // add another snap with instance key 14788 snapstate.Set(s.state, "some-snap_other", &snapstate.SnapState{ 14789 Active: true, 14790 Sequence: []*snap.SideInfo{ 14791 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 14792 }, 14793 Current: snap.R(3), 14794 SnapType: "app", 14795 InstanceKey: "other", 14796 }) 14797 other, err = snapstate.HasOtherInstances(s.state, "some-snap") 14798 c.Assert(err, IsNil) 14799 c.Assert(other, Equals, true) 14800 other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance") 14801 c.Assert(err, IsNil) 14802 c.Assert(other, Equals, true) 14803 } 14804 14805 func (s *snapmgrTestSuite) TestRequestSalt(c *C) { 14806 si := snap.SideInfo{ 14807 RealName: "other-snap", 14808 Revision: snap.R(7), 14809 SnapID: "other-snap-id", 14810 } 14811 s.state.Lock() 14812 defer s.state.Unlock() 14813 14814 snapstate.Set(s.state, "other-snap", &snapstate.SnapState{ 14815 Active: true, 14816 Sequence: []*snap.SideInfo{&si}, 14817 Current: si.Revision, 14818 SnapType: "app", 14819 }) 14820 snapstate.Set(s.state, "other-snap_instance", &snapstate.SnapState{ 14821 Active: true, 14822 Sequence: []*snap.SideInfo{&si}, 14823 Current: si.Revision, 14824 SnapType: "app", 14825 InstanceKey: "instance", 14826 }) 14827 14828 // clear request-salt to have it generated 14829 s.state.Set("refresh-privacy-key", nil) 14830 14831 _, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, s.user.ID, snapstate.Flags{}) 14832 c.Assert(err, ErrorMatches, "internal error: request salt is unset") 14833 14834 s.state.Set("refresh-privacy-key", "privacy-key") 14835 14836 chg := s.state.NewChange("install", "install a snap") 14837 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, s.user.ID, snapstate.Flags{}) 14838 c.Assert(err, IsNil) 14839 chg.AddAll(ts) 14840 14841 s.state.Unlock() 14842 defer s.se.Stop() 14843 s.settle(c) 14844 s.state.Lock() 14845 14846 c.Assert(len(s.fakeBackend.ops) >= 1, Equals, true) 14847 storeAction := s.fakeBackend.ops[0] 14848 c.Assert(storeAction.op, Equals, "storesvc-snap-action") 14849 c.Assert(storeAction.curSnaps, HasLen, 2) 14850 c.Assert(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true) 14851 } 14852 14853 type canDisableSuite struct{} 14854 14855 var _ = Suite(&canDisableSuite{}) 14856 14857 func (s *canDisableSuite) TestCanDisable(c *C) { 14858 for _, tt := range []struct { 14859 typ snap.Type 14860 canDisable bool 14861 }{ 14862 {snap.TypeApp, true}, 14863 {snap.TypeGadget, false}, 14864 {snap.TypeKernel, false}, 14865 {snap.TypeOS, false}, 14866 } { 14867 info := &snap.Info{SnapType: tt.typ} 14868 c.Check(snapstate.CanDisable(info), Equals, tt.canDisable) 14869 } 14870 } 14871 14872 func (s *snapmgrTestSuite) TestGadgetConnections(c *C) { 14873 r := release.MockOnClassic(false) 14874 defer r() 14875 14876 // using MockSnap, we want to read the bits on disk 14877 snapstate.MockSnapReadInfo(snap.ReadInfo) 14878 14879 deviceCtxNoGadget := deviceWithoutGadgetContext() 14880 deviceCtx := deviceWithGadgetContext("the-gadget") 14881 14882 s.state.Lock() 14883 defer s.state.Unlock() 14884 14885 _, err := snapstate.GadgetConnections(s.state, deviceCtxNoGadget) 14886 c.Assert(err, Equals, state.ErrNoState) 14887 14888 _, err = snapstate.GadgetConnections(s.state, deviceCtx) 14889 c.Assert(err, Equals, state.ErrNoState) 14890 14891 s.prepareGadget(c, ` 14892 connections: 14893 - plug: snap1idididididididididididididi:plug 14894 slot: snap2idididididididididididididi:slot 14895 `) 14896 14897 conns, err := snapstate.GadgetConnections(s.state, deviceCtx) 14898 c.Assert(err, IsNil) 14899 c.Check(conns, DeepEquals, []gadget.Connection{ 14900 {Plug: gadget.ConnectionPlug{SnapID: "snap1idididididididididididididi", Plug: "plug"}, Slot: gadget.ConnectionSlot{SnapID: "snap2idididididididididididididi", Slot: "slot"}}}) 14901 } 14902 14903 func (s *snapmgrTestSuite) TestSnapManagerCanStandby(c *C) { 14904 s.state.Lock() 14905 defer s.state.Unlock() 14906 14907 // no snaps -> can standby 14908 s.state.Set("snaps", nil) 14909 c.Assert(s.snapmgr.CanStandby(), Equals, true) 14910 14911 // snaps installed -> can *not* standby 14912 snapstate.Set(s.state, "core", &snapstate.SnapState{ 14913 Active: true, 14914 Sequence: []*snap.SideInfo{ 14915 {RealName: "core", Revision: snap.R(1)}, 14916 }, 14917 Current: snap.R(1), 14918 SnapType: "os", 14919 }) 14920 c.Assert(s.snapmgr.CanStandby(), Equals, false) 14921 } 14922 14923 func (s *snapmgrTestSuite) TestResolveChannelPinnedTrack(c *C) { 14924 for _, tc := range []struct { 14925 snap string 14926 new string 14927 exp string 14928 kernelTrack string 14929 gadgetTrack string 14930 err string 14931 }{ 14932 // neither kernel nor gadget 14933 {snap: "some-snap"}, 14934 {snap: "some-snap", new: "stable", exp: "stable"}, 14935 {snap: "some-snap", new: "foo/stable", exp: "foo/stable"}, 14936 {snap: "some-snap", new: "stable/with-branch", exp: "stable/with-branch"}, 14937 {snap: "some-snap", new: "supertrack/stable", exp: "supertrack/stable"}, 14938 {snap: "some-snap", new: "supertrack/stable/with-branch", exp: "supertrack/stable/with-branch"}, 14939 // kernel or gadget snap set, but unrelated snap 14940 {snap: "some-snap", new: "stable", exp: "stable", kernelTrack: "18"}, 14941 {snap: "some-snap", new: "foo/stable", exp: "foo/stable", kernelTrack: "18"}, 14942 {snap: "some-snap", new: "foo/stable", exp: "foo/stable", gadgetTrack: "18"}, 14943 // no pinned track 14944 {snap: "kernel", new: "latest/stable", exp: "latest/stable"}, 14945 {snap: "kernel", new: "stable", exp: "stable"}, 14946 {snap: "brand-gadget", new: "stable", exp: "stable"}, 14947 // not a risk only request 14948 {snap: "kernel", new: "", kernelTrack: "18"}, 14949 {snap: "brand-gadget", new: "", gadgetTrack: "18"}, 14950 {snap: "kernel", new: "latest/stable", kernelTrack: "18", err: "cannot switch from kernel track.*"}, 14951 {snap: "kernel", new: "latest/stable/hotfix-123", kernelTrack: "18", err: "cannot switch from kernel track.*"}, 14952 {snap: "kernel", new: "foo/stable", kernelTrack: "18", err: "cannot switch from kernel track.*"}, 14953 {snap: "brand-gadget", new: "foo/stable", exp: "18/stable", gadgetTrack: "18", err: "cannot switch from gadget track.*"}, 14954 {snap: "kernel", new: "18/stable", exp: "18/stable", kernelTrack: "18"}, 14955 {snap: "kernel", new: "18/stable", exp: "18/stable"}, 14956 {snap: "brand-gadget", new: "18/stable", exp: "18/stable", gadgetTrack: "18"}, 14957 {snap: "brand-gadget", new: "18/stable", exp: "18/stable"}, 14958 // risk/branch within a track 14959 {snap: "kernel", new: "stable/hotfix-123", exp: "18/stable/hotfix-123", kernelTrack: "18"}, 14960 {snap: "kernel", new: "18/stable/hotfix-123", exp: "18/stable/hotfix-123", kernelTrack: "18"}, 14961 // risk only defaults to pinned gadget track 14962 {snap: "brand-gadget", new: "stable", exp: "17/stable", gadgetTrack: "17"}, 14963 {snap: "brand-gadget", new: "edge", exp: "17/edge", gadgetTrack: "17"}, 14964 // risk only defaults to pinned kernel track 14965 {snap: "kernel", new: "stable", exp: "17/stable", kernelTrack: "17"}, 14966 {snap: "kernel", new: "edge", exp: "17/edge", kernelTrack: "17"}, 14967 } { 14968 c.Logf("tc: %+v", tc) 14969 if tc.kernelTrack != "" && tc.gadgetTrack != "" { 14970 c.Fatalf("setting both kernel and gadget tracks is not supported by the test") 14971 } 14972 var model *asserts.Model 14973 switch { 14974 case tc.kernelTrack != "": 14975 model = ModelWithKernelTrack(tc.kernelTrack) 14976 case tc.gadgetTrack != "": 14977 model = ModelWithGadgetTrack(tc.gadgetTrack) 14978 default: 14979 model = DefaultModel() 14980 } 14981 deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model} 14982 s.state.Lock() 14983 ch, err := snapstate.ResolveChannel(s.state, tc.snap, tc.new, deviceCtx) 14984 s.state.Unlock() 14985 if tc.err != "" { 14986 c.Check(err, ErrorMatches, tc.err) 14987 } else { 14988 c.Check(err, IsNil) 14989 c.Check(ch, Equals, tc.exp) 14990 } 14991 } 14992 } 14993 14994 func (s *snapmgrTestSuite) TestInstallValidatesInstanceNames(c *C) { 14995 s.state.Lock() 14996 defer s.state.Unlock() 14997 14998 _, err := snapstate.Install(context.Background(), s.state, "foo--invalid", nil, 0, snapstate.Flags{}) 14999 c.Assert(err, ErrorMatches, `invalid instance name: invalid snap name: "foo--invalid"`) 15000 15001 _, err = snapstate.Install(context.Background(), s.state, "foo_123_456", nil, 0, snapstate.Flags{}) 15002 c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`) 15003 15004 _, _, err = snapstate.InstallMany(s.state, []string{"foo--invalid"}, 0) 15005 c.Assert(err, ErrorMatches, `invalid instance name: invalid snap name: "foo--invalid"`) 15006 15007 _, _, err = snapstate.InstallMany(s.state, []string{"foo_123_456"}, 0) 15008 c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`) 15009 15010 mockSnap := makeTestSnap(c, `name: some-snap 15011 version: 1.0 15012 epoch: 1* 15013 `) 15014 si := snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)} 15015 _, _, err = snapstate.InstallPath(s.state, &si, mockSnap, "some-snap_123_456", "", snapstate.Flags{}) 15016 c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`) 15017 } 15018 15019 func (s *snapmgrTestSuite) TestGadgetUpdateTaskAddedOnInstall(c *C) { 15020 restore := release.MockOnClassic(false) 15021 defer restore() 15022 15023 s.state.Lock() 15024 defer s.state.Unlock() 15025 15026 // task added on install 15027 ts, err := snapstate.Install(context.Background(), s.state, "brand-gadget", nil, 0, snapstate.Flags{}) 15028 c.Assert(err, IsNil) 15029 15030 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 15031 verifyInstallTasks(c, updatesGadget, 0, ts, s.state) 15032 } 15033 15034 func (s *snapmgrTestSuite) TestGadgetUpdateTaskAddedOnRefresh(c *C) { 15035 restore := release.MockOnClassic(false) 15036 defer restore() 15037 15038 s.state.Lock() 15039 defer s.state.Unlock() 15040 15041 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 15042 Active: true, 15043 Sequence: []*snap.SideInfo{ 15044 {RealName: "brand-gadget", SnapID: "brand-gadget-id", Revision: snap.R(1)}, 15045 }, 15046 Current: snap.R(1), 15047 SnapType: "gadget", 15048 }) 15049 15050 // and on update 15051 ts, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{}, 0, snapstate.Flags{}) 15052 c.Assert(err, IsNil) 15053 15054 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 15055 verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh|updatesGadget, 0, ts, s.state) 15056 15057 } 15058 15059 func (s *snapmgrTestSuite) TestForSnapSetupResetsFlags(c *C) { 15060 flags := snapstate.Flags{ 15061 DevMode: true, 15062 JailMode: true, 15063 Classic: true, 15064 TryMode: true, 15065 Revert: true, 15066 RemoveSnapPath: true, 15067 IgnoreValidation: true, 15068 Required: true, 15069 SkipConfigure: true, 15070 Unaliased: true, 15071 Amend: true, 15072 IsAutoRefresh: true, 15073 NoReRefresh: true, 15074 RequireTypeBase: true, 15075 } 15076 flags = flags.ForSnapSetup() 15077 15078 // certain flags get reset, others are not touched 15079 c.Check(flags, DeepEquals, snapstate.Flags{ 15080 DevMode: true, 15081 JailMode: true, 15082 Classic: true, 15083 TryMode: true, 15084 Revert: true, 15085 RemoveSnapPath: true, 15086 IgnoreValidation: true, 15087 Required: true, 15088 SkipConfigure: false, 15089 Unaliased: true, 15090 Amend: true, 15091 IsAutoRefresh: true, 15092 NoReRefresh: false, 15093 RequireTypeBase: false, 15094 }) 15095 }