github.com/ubuntu-core/snappy@v0.0.0-20210827154228-9e584df982bb/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 "context" 24 "encoding/json" 25 "errors" 26 "fmt" 27 "io/ioutil" 28 "os" 29 "path/filepath" 30 "sort" 31 "strings" 32 "testing" 33 "time" 34 35 . "gopkg.in/check.v1" 36 "gopkg.in/tomb.v2" 37 38 "github.com/snapcore/snapd/asserts" 39 "github.com/snapcore/snapd/bootloader" 40 "github.com/snapcore/snapd/bootloader/bootloadertest" 41 "github.com/snapcore/snapd/dirs" 42 "github.com/snapcore/snapd/gadget" 43 "github.com/snapcore/snapd/interfaces" 44 "github.com/snapcore/snapd/logger" 45 "github.com/snapcore/snapd/overlord" 46 "github.com/snapcore/snapd/overlord/auth" 47 48 // So it registers Configure. 49 _ "github.com/snapcore/snapd/overlord/configstate" 50 "github.com/snapcore/snapd/overlord/configstate/config" 51 "github.com/snapcore/snapd/overlord/hookstate" 52 "github.com/snapcore/snapd/overlord/ifacestate/ifacerepo" 53 "github.com/snapcore/snapd/overlord/servicestate" 54 "github.com/snapcore/snapd/overlord/snapstate" 55 "github.com/snapcore/snapd/overlord/snapstate/backend" 56 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 57 "github.com/snapcore/snapd/overlord/state" 58 "github.com/snapcore/snapd/release" 59 "github.com/snapcore/snapd/sandbox" 60 "github.com/snapcore/snapd/snap" 61 "github.com/snapcore/snapd/snap/snaptest" 62 "github.com/snapcore/snapd/store" 63 "github.com/snapcore/snapd/testutil" 64 "github.com/snapcore/snapd/timeutil" 65 ) 66 67 func TestSnapManager(t *testing.T) { TestingT(t) } 68 69 type snapmgrTestSuite struct { 70 testutil.BaseTest 71 o *overlord.Overlord 72 state *state.State 73 se *overlord.StateEngine 74 snapmgr *snapstate.SnapManager 75 76 fakeBackend *fakeSnappyBackend 77 fakeStore *fakeStore 78 79 bl *bootloadertest.MockBootloader 80 81 user *auth.UserState 82 user2 *auth.UserState 83 user3 *auth.UserState 84 } 85 86 func (s *snapmgrTestSuite) settle(c *C) { 87 err := s.o.Settle(testutil.HostScaledTimeout(5 * time.Second)) 88 c.Assert(err, IsNil) 89 } 90 91 var _ = Suite(&snapmgrTestSuite{}) 92 93 var fakeRevDateEpoch = time.Date(2018, 1, 0, 0, 0, 0, 0, time.UTC) 94 95 func (s *snapmgrTestSuite) SetUpTest(c *C) { 96 s.BaseTest.SetUpTest(c) 97 dirs.SetRootDir(c.MkDir()) 98 99 s.o = overlord.Mock() 100 s.state = s.o.State() 101 102 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 103 104 restoreCheckFreeSpace := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return nil }) 105 s.AddCleanup(restoreCheckFreeSpace) 106 107 s.fakeBackend = &fakeSnappyBackend{} 108 s.fakeBackend.emptyContainer = emptyContainer(c) 109 s.fakeStore = &fakeStore{ 110 fakeCurrentProgress: 75, 111 fakeTotalProgress: 100, 112 fakeBackend: s.fakeBackend, 113 state: s.state, 114 } 115 116 // setup a bootloader for policy and boot 117 s.bl = bootloadertest.Mock("mock", c.MkDir()) 118 bootloader.Force(s.bl) 119 s.AddCleanup(func() { bootloader.Force(nil) }) 120 121 oldSetupInstallHook := snapstate.SetupInstallHook 122 oldSetupPreRefreshHook := snapstate.SetupPreRefreshHook 123 oldSetupPostRefreshHook := snapstate.SetupPostRefreshHook 124 oldSetupRemoveHook := snapstate.SetupRemoveHook 125 oldSnapServiceOptions := snapstate.SnapServiceOptions 126 oldEnsureSnapAbsentFromQuotaGroup := snapstate.EnsureSnapAbsentFromQuotaGroup 127 snapstate.SetupInstallHook = hookstate.SetupInstallHook 128 snapstate.SetupPreRefreshHook = hookstate.SetupPreRefreshHook 129 snapstate.SetupPostRefreshHook = hookstate.SetupPostRefreshHook 130 snapstate.SetupRemoveHook = hookstate.SetupRemoveHook 131 snapstate.SnapServiceOptions = servicestate.SnapServiceOptions 132 snapstate.EnsureSnapAbsentFromQuotaGroup = servicestate.EnsureSnapAbsentFromQuota 133 134 var err error 135 s.snapmgr, err = snapstate.Manager(s.state, s.o.TaskRunner()) 136 c.Assert(err, IsNil) 137 138 AddForeignTaskHandlers(s.o.TaskRunner(), s.fakeBackend) 139 140 snapstate.SetSnapManagerBackend(s.snapmgr, s.fakeBackend) 141 142 s.o.AddManager(s.snapmgr) 143 s.o.AddManager(s.o.TaskRunner()) 144 s.se = s.o.StateEngine() 145 c.Assert(s.o.StartUp(), IsNil) 146 147 s.BaseTest.AddCleanup(snapstate.MockSnapReadInfo(s.fakeBackend.ReadInfo)) 148 s.BaseTest.AddCleanup(snapstate.MockOpenSnapFile(s.fakeBackend.OpenSnapFile)) 149 revDate := func(info *snap.Info) time.Time { 150 if info.Revision.Local() { 151 panic("no local revision should reach revisionDate") 152 } 153 // for convenience a date derived from the revision 154 return fakeRevDateEpoch.AddDate(0, 0, info.Revision.N) 155 } 156 s.BaseTest.AddCleanup(snapstate.MockRevisionDate(revDate)) 157 158 s.BaseTest.AddCleanup(func() { 159 snapstate.SetupInstallHook = oldSetupInstallHook 160 snapstate.SetupPreRefreshHook = oldSetupPreRefreshHook 161 snapstate.SetupPostRefreshHook = oldSetupPostRefreshHook 162 snapstate.SetupRemoveHook = oldSetupRemoveHook 163 snapstate.SnapServiceOptions = oldSnapServiceOptions 164 snapstate.EnsureSnapAbsentFromQuotaGroup = oldEnsureSnapAbsentFromQuotaGroup 165 166 dirs.SetRootDir("/") 167 }) 168 169 s.BaseTest.AddCleanup(snapstate.MockReRefreshRetryTimeout(time.Second / 200)) 170 s.BaseTest.AddCleanup(snapstate.MockReRefreshUpdateMany(func(context.Context, *state.State, []string, int, snapstate.UpdateFilter, *snapstate.Flags, string) ([]string, []*state.TaskSet, error) { 171 return nil, nil, nil 172 })) 173 174 oldEstimateSnapshotSize := snapstate.EstimateSnapshotSize 175 snapstate.EstimateSnapshotSize = func(st *state.State, instanceName string, users []string) (uint64, error) { 176 return 1, nil 177 } 178 restoreInstallSize := snapstate.MockInstallSize(func(st *state.State, snaps []snapstate.MinimalInstallInfo, userID int) (uint64, error) { 179 return 0, nil 180 }) 181 s.AddCleanup(restoreInstallSize) 182 183 oldAutomaticSnapshot := snapstate.AutomaticSnapshot 184 snapstate.AutomaticSnapshot = func(st *state.State, instanceName string) (ts *state.TaskSet, err error) { 185 task := st.NewTask("save-snapshot", "...") 186 ts = state.NewTaskSet(task) 187 return ts, nil 188 } 189 190 oldAutomaticSnapshotExpiration := snapstate.AutomaticSnapshotExpiration 191 snapstate.AutomaticSnapshotExpiration = func(st *state.State) (time.Duration, error) { return 1, nil } 192 s.BaseTest.AddCleanup(func() { 193 snapstate.EstimateSnapshotSize = oldEstimateSnapshotSize 194 snapstate.AutomaticSnapshot = oldAutomaticSnapshot 195 snapstate.AutomaticSnapshotExpiration = oldAutomaticSnapshotExpiration 196 }) 197 198 s.state.Lock() 199 snapstate.ReplaceStore(s.state, s.fakeStore) 200 s.user, err = auth.NewUser(s.state, "username", "email@test.com", "macaroon", []string{"discharge"}) 201 c.Assert(err, IsNil) 202 s.user2, err = auth.NewUser(s.state, "username2", "email2@test.com", "macaroon2", []string{"discharge2"}) 203 c.Assert(err, IsNil) 204 // 3 has no store auth 205 s.user3, err = auth.NewUser(s.state, "username3", "email2@test.com", "", nil) 206 c.Assert(err, IsNil) 207 208 s.state.Set("seeded", true) 209 s.state.Set("seed-time", time.Now()) 210 211 r := snapstatetest.MockDeviceModel(DefaultModel()) 212 s.BaseTest.AddCleanup(r) 213 214 s.state.Set("refresh-privacy-key", "privacy-key") 215 snapstate.Set(s.state, "core", &snapstate.SnapState{ 216 Active: true, 217 Sequence: []*snap.SideInfo{ 218 {RealName: "core", Revision: snap.R(1)}, 219 }, 220 Current: snap.R(1), 221 SnapType: "os", 222 }) 223 224 repo := interfaces.NewRepository() 225 ifacerepo.Replace(s.state, repo) 226 s.state.Unlock() 227 228 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 229 return nil, nil 230 } 231 232 s.AddCleanup(snapstate.MockSecurityProfilesDiscardLate(func(snapName string, rev snap.Revision, typ snap.Type) error { 233 return nil 234 })) 235 } 236 237 func (s *snapmgrTestSuite) TearDownTest(c *C) { 238 s.BaseTest.TearDownTest(c) 239 snapstate.ValidateRefreshes = nil 240 snapstate.AutoAliases = nil 241 snapstate.CanAutoRefresh = nil 242 } 243 244 type ForeignTaskTracker interface { 245 ForeignTask(kind string, status state.Status, snapsup *snapstate.SnapSetup) 246 } 247 248 func AddForeignTaskHandlers(runner *state.TaskRunner, tracker ForeignTaskTracker) { 249 // Add fake handlers for tasks handled by interfaces manager 250 fakeHandler := func(task *state.Task, _ *tomb.Tomb) error { 251 task.State().Lock() 252 kind := task.Kind() 253 status := task.Status() 254 snapsup, err := snapstate.TaskSnapSetup(task) 255 task.State().Unlock() 256 if err != nil { 257 return err 258 } 259 260 tracker.ForeignTask(kind, status, snapsup) 261 262 return nil 263 } 264 runner.AddHandler("setup-profiles", fakeHandler, fakeHandler) 265 runner.AddHandler("auto-connect", fakeHandler, nil) 266 runner.AddHandler("auto-disconnect", fakeHandler, nil) 267 runner.AddHandler("remove-profiles", fakeHandler, fakeHandler) 268 runner.AddHandler("discard-conns", fakeHandler, fakeHandler) 269 runner.AddHandler("validate-snap", fakeHandler, nil) 270 runner.AddHandler("transition-ubuntu-core", fakeHandler, nil) 271 runner.AddHandler("transition-to-snapd-snap", fakeHandler, nil) 272 runner.AddHandler("update-gadget-assets", fakeHandler, nil) 273 274 // Add handler to test full aborting of changes 275 erroringHandler := func(task *state.Task, _ *tomb.Tomb) error { 276 return errors.New("error out") 277 } 278 runner.AddHandler("error-trigger", erroringHandler, nil) 279 280 runner.AddHandler("save-snapshot", func(task *state.Task, _ *tomb.Tomb) error { 281 return nil 282 }, nil) 283 runner.AddHandler("run-hook", func(task *state.Task, _ *tomb.Tomb) error { 284 return nil 285 }, nil) 286 runner.AddHandler("configure-snapd", func(t *state.Task, _ *tomb.Tomb) error { 287 return nil 288 }, nil) 289 290 } 291 292 func (s *snapmgrTestSuite) TestCleanSnapStateGet(c *C) { 293 snapst := snapstate.SnapState{ 294 Sequence: []*snap.SideInfo{ 295 {RealName: "foo", Revision: snap.R(1)}, 296 }, 297 Current: snap.R(1), 298 SnapType: "os", 299 TrackingChannel: "foo/stable", 300 InstanceKey: "bar", 301 } 302 303 s.state.Lock() 304 305 defer s.state.Unlock() 306 snapstate.Set(s.state, "no-instance-key", &snapstate.SnapState{ 307 Sequence: []*snap.SideInfo{ 308 {RealName: "core", Revision: snap.R(1)}, 309 }, 310 Current: snap.R(1), 311 SnapType: "app", 312 }) 313 314 err := snapstate.Get(s.state, "bar", nil) 315 c.Assert(err, ErrorMatches, "internal error: snapst is nil") 316 317 err = snapstate.Get(s.state, "no-instance-key", &snapst) 318 c.Assert(err, IsNil) 319 c.Assert(snapst, DeepEquals, snapstate.SnapState{ 320 Sequence: []*snap.SideInfo{ 321 {RealName: "core", Revision: snap.R(1)}, 322 }, 323 Current: snap.R(1), 324 SnapType: "app", 325 }) 326 } 327 328 func (s *snapmgrTestSuite) TestStore(c *C) { 329 s.state.Lock() 330 defer s.state.Unlock() 331 332 sto := &store.Store{} 333 snapstate.ReplaceStore(s.state, sto) 334 store1 := snapstate.Store(s.state, nil) 335 c.Check(store1, Equals, sto) 336 337 // cached 338 store2 := snapstate.Store(s.state, nil) 339 c.Check(store2, Equals, sto) 340 } 341 342 func (s *snapmgrTestSuite) TestStoreWithDeviceContext(c *C) { 343 s.state.Lock() 344 defer s.state.Unlock() 345 346 stoA := &store.Store{} 347 snapstate.ReplaceStore(s.state, stoA) 348 store1 := snapstate.Store(s.state, nil) 349 c.Check(store1, Equals, stoA) 350 351 stoB := &store.Store{} 352 353 // cached 354 store2 := snapstate.Store(s.state, &snapstatetest.TrivialDeviceContext{}) 355 c.Check(store2, Equals, stoA) 356 357 // from context 358 store3 := snapstate.Store(s.state, &snapstatetest.TrivialDeviceContext{CtxStore: stoB}) 359 c.Check(store3, Equals, stoB) 360 } 361 362 func (s *snapmgrTestSuite) TestUserFromUserID(c *C) { 363 s.state.Lock() 364 defer s.state.Unlock() 365 366 tests := []struct { 367 ids []int 368 u *auth.UserState 369 invalid bool 370 }{ 371 {[]int{0}, nil, false}, 372 {[]int{2}, s.user2, false}, 373 {[]int{99}, nil, true}, 374 {[]int{1, 99}, s.user, false}, 375 {[]int{99, 0}, nil, false}, 376 {[]int{99, 2}, s.user2, false}, 377 {[]int{99, 100}, nil, true}, 378 } 379 380 for _, t := range tests { 381 u, err := snapstate.UserFromUserID(s.state, t.ids...) 382 c.Check(u, DeepEquals, t.u) 383 if t.invalid { 384 c.Check(err, Equals, auth.ErrInvalidUser) 385 } else { 386 c.Check(err, IsNil) 387 } 388 } 389 } 390 391 const ( 392 unlinkBefore = 1 << iota 393 cleanupAfter 394 maybeCore 395 runCoreConfigure 396 doesReRefresh 397 updatesGadget 398 updatesBootConfig 399 noConfigure 400 ) 401 402 func taskKinds(tasks []*state.Task) []string { 403 kinds := make([]string, len(tasks)) 404 for i, task := range tasks { 405 k := task.Kind() 406 if k == "run-hook" { 407 var hooksup hookstate.HookSetup 408 if err := task.Get("hook-setup", &hooksup); err != nil { 409 panic(err) 410 } 411 k = fmt.Sprintf("%s[%s]", k, hooksup.Hook) 412 } 413 kinds[i] = k 414 } 415 return kinds 416 } 417 418 func verifyLastTasksetIsReRefresh(c *C, tts []*state.TaskSet) { 419 ts := tts[len(tts)-1] 420 c.Assert(ts.Tasks(), HasLen, 1) 421 reRefresh := ts.Tasks()[0] 422 c.Check(reRefresh.Kind(), Equals, "check-rerefresh") 423 // nothing should wait on it 424 c.Check(reRefresh.NumHaltTasks(), Equals, 0) 425 } 426 427 func verifyRemoveTasks(c *C, ts *state.TaskSet) { 428 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 429 "stop-snap-services", 430 "run-hook[remove]", 431 "auto-disconnect", 432 "save-snapshot", 433 "remove-aliases", 434 "unlink-snap", 435 "remove-profiles", 436 "clear-snap", 437 "discard-snap", 438 }) 439 verifyStopReason(c, ts, "remove") 440 } 441 442 func verifyCoreRemoveTasks(c *C, ts *state.TaskSet) { 443 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 444 "stop-snap-services", 445 "run-hook[remove]", 446 "auto-disconnect", 447 "remove-aliases", 448 "unlink-snap", 449 "remove-profiles", 450 "clear-snap", 451 "discard-snap", 452 }) 453 verifyStopReason(c, ts, "remove") 454 } 455 456 func checkIsAutoRefresh(c *C, tasks []*state.Task, expected bool) { 457 for _, t := range tasks { 458 if t.Kind() == "download-snap" { 459 var snapsup snapstate.SnapSetup 460 err := t.Get("snap-setup", &snapsup) 461 c.Assert(err, IsNil) 462 c.Check(snapsup.IsAutoRefresh, Equals, expected) 463 return 464 } 465 } 466 c.Fatalf("cannot find download-snap task in %v", tasks) 467 } 468 469 func (s *snapmgrTestSuite) TestLastIndexFindsLast(c *C) { 470 snapst := &snapstate.SnapState{Sequence: []*snap.SideInfo{ 471 {Revision: snap.R(7)}, 472 {Revision: snap.R(11)}, 473 {Revision: snap.R(11)}, 474 }} 475 c.Check(snapst.LastIndex(snap.R(11)), Equals, 2) 476 } 477 478 func maybeMockClassicSupport(c *C) (restore func()) { 479 if dirs.SupportsClassicConfinement() { 480 return func() {} 481 } 482 483 d := filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/snap") 484 err := os.MkdirAll(d, 0755) 485 c.Assert(err, IsNil) 486 snapSymlink := filepath.Join(dirs.GlobalRootDir, "snap") 487 err = os.Symlink(d, snapSymlink) 488 c.Assert(err, IsNil) 489 490 return func() { os.Remove(snapSymlink) } 491 } 492 493 type fullFlags struct{ before, change, after, setup snapstate.Flags } 494 495 func (s *snapmgrTestSuite) testRevertTasksFullFlags(flags fullFlags, c *C) { 496 s.state.Lock() 497 defer s.state.Unlock() 498 499 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 500 Active: true, 501 Sequence: []*snap.SideInfo{ 502 {RealName: "some-snap", Revision: snap.R(7)}, 503 {RealName: "some-snap", Revision: snap.R(11)}, 504 }, 505 Flags: flags.before, 506 Current: snap.R(11), 507 SnapType: "app", 508 }) 509 510 ts, err := snapstate.Revert(s.state, "some-snap", flags.change) 511 c.Assert(err, IsNil) 512 513 tasks := ts.Tasks() 514 c.Assert(s.state.TaskCount(), Equals, len(tasks)) 515 c.Assert(taskKinds(tasks), DeepEquals, []string{ 516 "prerequisites", 517 "prepare-snap", 518 "stop-snap-services", 519 "remove-aliases", 520 "unlink-current-snap", 521 "setup-profiles", 522 "link-snap", 523 "auto-connect", 524 "set-auto-aliases", 525 "setup-aliases", 526 "start-snap-services", 527 "run-hook[configure]", 528 "run-hook[check-health]", 529 }) 530 // a revert is a special refresh 531 verifyStopReason(c, ts, "refresh") 532 533 snapsup, err := snapstate.TaskSnapSetup(tasks[0]) 534 c.Assert(err, IsNil) 535 flags.setup.Revert = true 536 c.Check(snapsup.Flags, Equals, flags.setup) 537 c.Check(snapsup.Type, Equals, snap.TypeApp) 538 539 chg := s.state.NewChange("revert", "revert snap") 540 chg.AddAll(ts) 541 542 s.state.Unlock() 543 defer s.se.Stop() 544 s.settle(c) 545 s.state.Lock() 546 547 var snapst snapstate.SnapState 548 err = snapstate.Get(s.state, "some-snap", &snapst) 549 c.Assert(err, IsNil) 550 c.Check(snapst.Flags, Equals, flags.after) 551 } 552 553 func (s *snapmgrTestSuite) testRevertTasks(flags snapstate.Flags, c *C) { 554 s.testRevertTasksFullFlags(fullFlags{before: flags, change: flags, after: flags, setup: flags}, c) 555 } 556 557 func (s *snapmgrTestSuite) TestRevertTasks(c *C) { 558 s.testRevertTasks(snapstate.Flags{}, c) 559 } 560 561 func (s *snapmgrTestSuite) TestRevertTasksFromDevMode(c *C) { 562 // the snap is installed in devmode, but the request to revert does not specify devmode 563 s.testRevertTasksFullFlags(fullFlags{ 564 before: snapstate.Flags{DevMode: true}, // the snap is installed in devmode 565 change: snapstate.Flags{}, // the request to revert does not specify devmode 566 after: snapstate.Flags{DevMode: true}, // the reverted snap is installed in devmode 567 setup: snapstate.Flags{DevMode: true}, // because setup said so 568 }, c) 569 } 570 571 func (s *snapmgrTestSuite) TestRevertTasksFromJailMode(c *C) { 572 // the snap is installed in jailmode, but the request to revert does not specify jailmode 573 s.testRevertTasksFullFlags(fullFlags{ 574 before: snapstate.Flags{JailMode: true}, // the snap is installed in jailmode 575 change: snapstate.Flags{}, // the request to revert does not specify jailmode 576 after: snapstate.Flags{JailMode: true}, // the reverted snap is installed in jailmode 577 setup: snapstate.Flags{JailMode: true}, // because setup said so 578 }, c) 579 } 580 581 func (s *snapmgrTestSuite) TestRevertTasksFromClassic(c *C) { 582 restore := maybeMockClassicSupport(c) 583 defer restore() 584 585 // the snap is installed in classic, but the request to revert does not specify classic 586 s.testRevertTasksFullFlags(fullFlags{ 587 before: snapstate.Flags{Classic: true}, // the snap is installed in classic 588 change: snapstate.Flags{}, // the request to revert does not specify classic 589 after: snapstate.Flags{Classic: true}, // the reverted snap is installed in classic 590 setup: snapstate.Flags{Classic: true}, // because setup said so 591 }, c) 592 } 593 594 func (s *snapmgrTestSuite) TestRevertTasksDevMode(c *C) { 595 s.testRevertTasks(snapstate.Flags{DevMode: true}, c) 596 } 597 598 func (s *snapmgrTestSuite) TestRevertTasksJailMode(c *C) { 599 s.testRevertTasks(snapstate.Flags{JailMode: true}, c) 600 } 601 602 func (s *snapmgrTestSuite) TestRevertTasksClassic(c *C) { 603 restore := maybeMockClassicSupport(c) 604 defer restore() 605 606 s.testRevertTasks(snapstate.Flags{Classic: true}, c) 607 } 608 609 func (s *snapmgrTestSuite) TestRevertCreatesNoGCTasks(c *C) { 610 s.state.Lock() 611 defer s.state.Unlock() 612 613 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 614 Active: true, 615 SnapType: "app", 616 Sequence: []*snap.SideInfo{ 617 {RealName: "some-snap", Revision: snap.R(1)}, 618 {RealName: "some-snap", Revision: snap.R(2)}, 619 {RealName: "some-snap", Revision: snap.R(3)}, 620 {RealName: "some-snap", Revision: snap.R(4)}, 621 }, 622 Current: snap.R(2), 623 }) 624 625 ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(4), snapstate.Flags{}) 626 c.Assert(err, IsNil) 627 628 // ensure that we do not run any form of garbage-collection 629 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 630 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 631 "prerequisites", 632 "prepare-snap", 633 "stop-snap-services", 634 "remove-aliases", 635 "unlink-current-snap", 636 "setup-profiles", 637 "link-snap", 638 "auto-connect", 639 "set-auto-aliases", 640 "setup-aliases", 641 "start-snap-services", 642 "run-hook[configure]", 643 "run-hook[check-health]", 644 }) 645 } 646 647 func (s *snapmgrTestSuite) TestEnableTasks(c *C) { 648 s.state.Lock() 649 defer s.state.Unlock() 650 651 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 652 Sequence: []*snap.SideInfo{ 653 {RealName: "some-snap", Revision: snap.R(11)}, 654 }, 655 Current: snap.R(11), 656 Active: false, 657 }) 658 659 ts, err := snapstate.Enable(s.state, "some-snap") 660 c.Assert(err, IsNil) 661 662 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 663 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 664 "prepare-snap", 665 "setup-profiles", 666 "link-snap", 667 "setup-aliases", 668 "start-snap-services", 669 }) 670 } 671 672 func (s *snapmgrTestSuite) TestSwitchTasks(c *C) { 673 s.state.Lock() 674 defer s.state.Unlock() 675 676 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 677 Sequence: []*snap.SideInfo{ 678 {RealName: "some-snap", Revision: snap.R(11)}, 679 }, 680 Current: snap.R(11), 681 Active: false, 682 }) 683 684 ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}) 685 c.Assert(err, IsNil) 686 687 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 688 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{"switch-snap"}) 689 } 690 691 func (s *snapmgrTestSuite) TestSwitchConflict(c *C) { 692 s.state.Lock() 693 defer s.state.Unlock() 694 695 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 696 Sequence: []*snap.SideInfo{ 697 {RealName: "some-snap", Revision: snap.R(11)}, 698 }, 699 Current: snap.R(11), 700 Active: false, 701 }) 702 703 ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}) 704 c.Assert(err, IsNil) 705 // need a change to make the tasks visible 706 s.state.NewChange("switch-snap", "...").AddAll(ts) 707 708 _, err = snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "other-channel"}) 709 c.Check(err, ErrorMatches, `snap "some-snap" has "switch-snap" change in progress`) 710 } 711 712 func (s *snapmgrTestSuite) TestSwitchUnhappy(c *C) { 713 s.state.Lock() 714 defer s.state.Unlock() 715 716 _, err := snapstate.Switch(s.state, "non-existing-snap", &snapstate.RevisionOptions{Channel: "some-channel"}) 717 c.Assert(err, ErrorMatches, `snap "non-existing-snap" is not installed`) 718 } 719 720 func (s *snapmgrTestSuite) TestSwitchRevision(c *C) { 721 s.state.Lock() 722 defer s.state.Unlock() 723 724 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 725 Sequence: []*snap.SideInfo{ 726 {RealName: "some-snap", Revision: snap.R(11)}, 727 }, 728 Current: snap.R(11), 729 }) 730 731 _, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(42)}) 732 c.Assert(err, ErrorMatches, "cannot switch revision") 733 } 734 735 func (s *snapmgrTestSuite) TestSwitchKernelTrackForbidden(c *C) { 736 s.state.Lock() 737 defer s.state.Unlock() 738 739 r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 740 defer r() 741 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 742 Sequence: []*snap.SideInfo{ 743 {RealName: "kernel", Revision: snap.R(11)}, 744 }, 745 TrackingChannel: "18/stable", 746 Current: snap.R(11), 747 Active: true, 748 }) 749 750 _, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}) 751 c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`) 752 } 753 754 func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyIsOK(c *C) { 755 s.state.Lock() 756 defer s.state.Unlock() 757 758 r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 759 defer r() 760 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 761 Sequence: []*snap.SideInfo{ 762 {RealName: "kernel", Revision: snap.R(11)}, 763 }, 764 TrackingChannel: "18/stable", 765 Current: snap.R(11), 766 Active: true, 767 }) 768 769 _, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "18/beta"}) 770 c.Assert(err, IsNil) 771 } 772 773 func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyDefaultTrackIsOK(c *C) { 774 s.state.Lock() 775 defer s.state.Unlock() 776 777 r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 778 defer r() 779 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 780 Sequence: []*snap.SideInfo{ 781 {RealName: "kernel", Revision: snap.R(11)}, 782 }, 783 TrackingChannel: "18/stable", 784 Current: snap.R(11), 785 Active: true, 786 }) 787 788 _, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "beta"}) 789 c.Assert(err, IsNil) 790 } 791 792 func (s *snapmgrTestSuite) TestSwitchGadgetTrackForbidden(c *C) { 793 s.state.Lock() 794 defer s.state.Unlock() 795 796 r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18")) 797 defer r() 798 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 799 Sequence: []*snap.SideInfo{ 800 {RealName: "brand-gadget", Revision: snap.R(11)}, 801 }, 802 TrackingChannel: "18/stable", 803 Current: snap.R(11), 804 Active: true, 805 }) 806 807 _, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}) 808 c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`) 809 } 810 811 func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyIsOK(c *C) { 812 s.state.Lock() 813 defer s.state.Unlock() 814 815 r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18")) 816 defer r() 817 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 818 Sequence: []*snap.SideInfo{ 819 {RealName: "brand-gadget", Revision: snap.R(11)}, 820 }, 821 TrackingChannel: "18/stable", 822 Current: snap.R(11), 823 Active: true, 824 }) 825 826 _, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "18/beta"}) 827 c.Assert(err, IsNil) 828 } 829 830 func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyDefaultTrackIsOK(c *C) { 831 s.state.Lock() 832 defer s.state.Unlock() 833 834 r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18")) 835 defer r() 836 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 837 Sequence: []*snap.SideInfo{ 838 {RealName: "brand-gadget", Revision: snap.R(11)}, 839 }, 840 TrackingChannel: "18/stable", 841 Current: snap.R(11), 842 Active: true, 843 }) 844 845 _, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "beta"}) 846 c.Assert(err, IsNil) 847 } 848 849 func (s *snapmgrTestSuite) TestDisableTasks(c *C) { 850 s.state.Lock() 851 defer s.state.Unlock() 852 853 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 854 Sequence: []*snap.SideInfo{ 855 {RealName: "some-snap", Revision: snap.R(11)}, 856 }, 857 Current: snap.R(11), 858 Active: true, 859 }) 860 861 ts, err := snapstate.Disable(s.state, "some-snap") 862 c.Assert(err, IsNil) 863 864 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 865 c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{ 866 "stop-snap-services", 867 "remove-aliases", 868 "unlink-snap", 869 "remove-profiles", 870 }) 871 verifyStopReason(c, ts, "disable") 872 } 873 874 func (s *snapmgrTestSuite) TestEnableConflict(c *C) { 875 s.state.Lock() 876 defer s.state.Unlock() 877 878 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 879 Sequence: []*snap.SideInfo{ 880 {RealName: "some-snap", Revision: snap.R(11)}, 881 }, 882 Current: snap.R(11), 883 Active: false, 884 }) 885 886 ts, err := snapstate.Enable(s.state, "some-snap") 887 c.Assert(err, IsNil) 888 // need a change to make the tasks visible 889 s.state.NewChange("enable", "...").AddAll(ts) 890 891 _, err = snapstate.Enable(s.state, "some-snap") 892 c.Assert(err, ErrorMatches, `snap "some-snap" has "enable" change in progress`) 893 } 894 895 func (s *snapmgrTestSuite) TestDisableConflict(c *C) { 896 s.state.Lock() 897 defer s.state.Unlock() 898 899 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 900 Sequence: []*snap.SideInfo{ 901 {RealName: "some-snap", Revision: snap.R(11)}, 902 }, 903 Current: snap.R(11), 904 Active: true, 905 }) 906 907 ts, err := snapstate.Disable(s.state, "some-snap") 908 c.Assert(err, IsNil) 909 // need a change to make the tasks visible 910 s.state.NewChange("install", "...").AddAll(ts) 911 912 _, err = snapstate.Disable(s.state, "some-snap") 913 c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`) 914 } 915 916 func (s *snapmgrTestSuite) TestDoInstallWithSlots(c *C) { 917 s.state.Lock() 918 defer s.state.Unlock() 919 920 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 921 922 ts, err := snapstate.Install(context.Background(), s.state, "snap-content-slot", nil, 0, snapstate.Flags{}) 923 c.Assert(err, IsNil) 924 925 var snapsup snapstate.SnapSetup 926 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 927 c.Assert(err, IsNil) 928 929 c.Check(snapsup.PlugsOnly, Equals, false) 930 } 931 932 func (s *snapmgrTestSuite) TestDoUpdateHadSlots(c *C) { 933 s.state.Lock() 934 defer s.state.Unlock() 935 936 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 937 Active: true, 938 Sequence: []*snap.SideInfo{ 939 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}, 940 }, 941 Current: snap.R(4), 942 SnapType: "app", 943 }) 944 945 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 946 if name != "some-snap" { 947 return s.fakeBackend.ReadInfo(name, si) 948 } 949 950 info := &snap.Info{ 951 SideInfo: *si, 952 SnapType: snap.TypeApp, 953 } 954 info.Slots = map[string]*snap.SlotInfo{ 955 "some-slot": { 956 Snap: info, 957 Name: "shared-content", 958 Interface: "content", 959 Attrs: map[string]interface{}{ 960 "content": "shared-content", 961 }, 962 }, 963 } 964 return info, nil 965 }) 966 967 ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{}) 968 c.Assert(err, IsNil) 969 970 var snapsup snapstate.SnapSetup 971 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 972 c.Assert(err, IsNil) 973 974 c.Check(snapsup.PlugsOnly, Equals, false) 975 } 976 977 func (s *snapmgrTestSuite) TestDisableSnapDisabledServicesSaved(c *C) { 978 s.state.Lock() 979 defer s.state.Unlock() 980 981 prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled 982 s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"} 983 984 // reset the services to what they were before after the test is done 985 defer func() { 986 s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled 987 }() 988 989 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 990 Sequence: []*snap.SideInfo{ 991 {RealName: "services-snap", Revision: snap.R(11)}, 992 }, 993 Current: snap.R(11), 994 Active: true, 995 }) 996 997 disableChg := s.state.NewChange("disable", "disable a snap") 998 ts, err := snapstate.Disable(s.state, "services-snap") 999 c.Assert(err, IsNil) 1000 disableChg.AddAll(ts) 1001 1002 s.state.Unlock() 1003 defer s.se.Stop() 1004 s.settle(c) 1005 s.state.Lock() 1006 1007 c.Assert(disableChg.Err(), IsNil) 1008 c.Assert(disableChg.IsReady(), Equals, true) 1009 1010 // get the snap state 1011 var snapst snapstate.SnapState 1012 err = snapstate.Get(s.state, "services-snap", &snapst) 1013 c.Assert(err, IsNil) 1014 1015 // make sure that the disabled services in this snap's state is what we 1016 // provided 1017 sort.Strings(snapst.LastActiveDisabledServices) 1018 c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"}) 1019 } 1020 func (s *snapmgrTestSuite) TestEnableSnapDisabledServicesPassedAroundHappy(c *C) { 1021 s.state.Lock() 1022 defer s.state.Unlock() 1023 1024 prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled 1025 s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"} 1026 1027 // reset the services to what they were before after the test is done 1028 defer func() { 1029 s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled 1030 }() 1031 1032 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 1033 Sequence: []*snap.SideInfo{ 1034 {RealName: "services-snap", Revision: snap.R(11)}, 1035 }, 1036 Current: snap.R(11), 1037 Active: true, 1038 }) 1039 1040 disableChg := s.state.NewChange("disable", "disable a snap") 1041 disableTs, err := snapstate.Disable(s.state, "services-snap") 1042 c.Assert(err, IsNil) 1043 disableChg.AddAll(disableTs) 1044 1045 s.state.Unlock() 1046 defer s.se.Stop() 1047 s.settle(c) 1048 s.state.Lock() 1049 1050 c.Assert(disableChg.Err(), IsNil) 1051 c.Assert(disableChg.IsReady(), Equals, true) 1052 1053 // get the snap state 1054 var snapst snapstate.SnapState 1055 err = snapstate.Get(s.state, "services-snap", &snapst) 1056 c.Assert(err, IsNil) 1057 1058 // make sure that the disabled services in this snap's state is what we 1059 // provided 1060 sort.Strings(snapst.LastActiveDisabledServices) 1061 c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"}) 1062 1063 enableChg := s.state.NewChange("enable", "disable a snap") 1064 enableTs, err := snapstate.Enable(s.state, "services-snap") 1065 c.Assert(err, IsNil) 1066 enableChg.AddAll(enableTs) 1067 1068 s.state.Unlock() 1069 defer s.se.Stop() 1070 s.settle(c) 1071 s.state.Lock() 1072 1073 c.Assert(enableChg.Err(), IsNil) 1074 c.Assert(enableChg.IsReady(), Equals, true) 1075 1076 // check the ops that will be provided disabledServices 1077 svcStateOp := s.fakeBackend.ops.First("current-snap-service-states") 1078 c.Assert(svcStateOp, Not(IsNil)) 1079 c.Assert(svcStateOp.disabledServices, DeepEquals, []string{"svc1", "svc2"}) 1080 1081 linkStateOp := s.fakeBackend.ops.First("start-snap-services") 1082 c.Assert(linkStateOp, Not(IsNil)) 1083 c.Assert(linkStateOp.disabledServices, DeepEquals, []string{"svc1", "svc2"}) 1084 } 1085 1086 func (s *snapmgrTestSuite) TestEnableSnapDisabledServicesNotSaved(c *C) { 1087 s.state.Lock() 1088 defer s.state.Unlock() 1089 1090 prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled 1091 s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"} 1092 1093 // reset the services to what they were before after the test is done 1094 defer func() { 1095 s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled 1096 }() 1097 1098 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 1099 Sequence: []*snap.SideInfo{ 1100 {RealName: "services-snap", Revision: snap.R(11)}, 1101 }, 1102 Current: snap.R(11), 1103 Active: true, 1104 }) 1105 1106 disableChg := s.state.NewChange("disable", "disable a snap") 1107 disableTs, err := snapstate.Disable(s.state, "services-snap") 1108 c.Assert(err, IsNil) 1109 disableChg.AddAll(disableTs) 1110 1111 s.state.Unlock() 1112 defer s.se.Stop() 1113 s.settle(c) 1114 s.state.Lock() 1115 1116 c.Assert(disableChg.Err(), IsNil) 1117 c.Assert(disableChg.IsReady(), Equals, true) 1118 1119 // get the snap state 1120 var snapst snapstate.SnapState 1121 err = snapstate.Get(s.state, "services-snap", &snapst) 1122 c.Assert(err, IsNil) 1123 1124 // make sure that the disabled services in this snap's state is what we 1125 // provided 1126 sort.Strings(snapst.LastActiveDisabledServices) 1127 c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"}) 1128 1129 enableChg := s.state.NewChange("enable", "disable a snap") 1130 enableTs, err := snapstate.Enable(s.state, "services-snap") 1131 c.Assert(err, IsNil) 1132 enableChg.AddAll(enableTs) 1133 1134 s.state.Unlock() 1135 defer s.se.Stop() 1136 s.settle(c) 1137 s.state.Lock() 1138 1139 c.Assert(enableChg.Err(), IsNil) 1140 c.Assert(enableChg.IsReady(), Equals, true) 1141 1142 // get the snap state again 1143 err = snapstate.Get(s.state, "services-snap", &snapst) 1144 c.Assert(err, IsNil) 1145 1146 // make sure that there is nothing in the last active disabled services list 1147 // because we re-enabled the snap and there should be nothing we have to 1148 // keep track of in the state anymore 1149 c.Assert(snapst.LastActiveDisabledServices, HasLen, 0) 1150 } 1151 1152 func (s *snapmgrTestSuite) TestEnableSnapMissingDisabledServicesMergedAndSaved(c *C) { 1153 s.state.Lock() 1154 defer s.state.Unlock() 1155 1156 prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled 1157 s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"} 1158 1159 // reset the services to what they were before after the test is done 1160 defer func() { 1161 s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled 1162 }() 1163 1164 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 1165 Sequence: []*snap.SideInfo{ 1166 {RealName: "services-snap", Revision: snap.R(11)}, 1167 }, 1168 Current: snap.R(11), 1169 Active: true, 1170 // keep this to make gofmt 1.10 happy 1171 LastActiveDisabledServices: []string{"missing-svc3"}, 1172 }) 1173 1174 disableChg := s.state.NewChange("disable", "disable a snap") 1175 disableTs, err := snapstate.Disable(s.state, "services-snap") 1176 c.Assert(err, IsNil) 1177 disableChg.AddAll(disableTs) 1178 1179 s.state.Unlock() 1180 defer s.se.Stop() 1181 s.settle(c) 1182 s.state.Lock() 1183 1184 c.Assert(disableChg.Err(), IsNil) 1185 c.Assert(disableChg.IsReady(), Equals, true) 1186 1187 // get the snap state 1188 var snapst snapstate.SnapState 1189 err = snapstate.Get(s.state, "services-snap", &snapst) 1190 c.Assert(err, IsNil) 1191 1192 // make sure that the disabled services in this snap's state is what we 1193 // provided 1194 sort.Strings(snapst.LastActiveDisabledServices) 1195 c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3", "svc1", "svc2"}) 1196 1197 enableChg := s.state.NewChange("enable", "disable a snap") 1198 enableTs, err := snapstate.Enable(s.state, "services-snap") 1199 c.Assert(err, IsNil) 1200 enableChg.AddAll(enableTs) 1201 1202 s.state.Unlock() 1203 defer s.se.Stop() 1204 s.settle(c) 1205 s.state.Lock() 1206 1207 c.Assert(enableChg.Err(), IsNil) 1208 c.Assert(enableChg.IsReady(), Equals, true) 1209 1210 // get the snap state again 1211 err = snapstate.Get(s.state, "services-snap", &snapst) 1212 c.Assert(err, IsNil) 1213 1214 // make sure that there is nothing in the last active disabled services list 1215 // because we re-enabled the snap and there should be nothing we have to 1216 // keep track of in the state anymore 1217 c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3"}) 1218 } 1219 1220 func (s *snapmgrTestSuite) TestEnableSnapMissingDisabledServicesSaved(c *C) { 1221 s.state.Lock() 1222 defer s.state.Unlock() 1223 1224 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 1225 Sequence: []*snap.SideInfo{ 1226 {RealName: "services-snap", Revision: snap.R(11)}, 1227 }, 1228 Current: snap.R(11), 1229 Active: true, 1230 // keep this to make gofmt 1.10 happy 1231 LastActiveDisabledServices: []string{"missing-svc3"}, 1232 }) 1233 1234 disableChg := s.state.NewChange("disable", "disable a snap") 1235 disableTs, err := snapstate.Disable(s.state, "services-snap") 1236 c.Assert(err, IsNil) 1237 disableChg.AddAll(disableTs) 1238 1239 s.state.Unlock() 1240 defer s.se.Stop() 1241 s.settle(c) 1242 s.state.Lock() 1243 1244 c.Assert(disableChg.Err(), IsNil) 1245 c.Assert(disableChg.IsReady(), Equals, true) 1246 1247 // get the snap state 1248 var snapst snapstate.SnapState 1249 err = snapstate.Get(s.state, "services-snap", &snapst) 1250 c.Assert(err, IsNil) 1251 1252 // make sure that the disabled services in this snap's state is what we 1253 // provided 1254 sort.Strings(snapst.LastActiveDisabledServices) 1255 c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3"}) 1256 1257 enableChg := s.state.NewChange("enable", "disable a snap") 1258 enableTs, err := snapstate.Enable(s.state, "services-snap") 1259 c.Assert(err, IsNil) 1260 enableChg.AddAll(enableTs) 1261 1262 s.state.Unlock() 1263 defer s.se.Stop() 1264 s.settle(c) 1265 s.state.Lock() 1266 1267 c.Assert(enableChg.Err(), IsNil) 1268 c.Assert(enableChg.IsReady(), Equals, true) 1269 1270 // get the snap state again 1271 err = snapstate.Get(s.state, "services-snap", &snapst) 1272 c.Assert(err, IsNil) 1273 1274 // make sure that there is nothing in the last active disabled services list 1275 // because we re-enabled the snap and there should be nothing we have to 1276 // keep track of in the state anymore 1277 c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3"}) 1278 } 1279 1280 func makeTestSnap(c *C, snapYamlContent string) (snapFilePath string) { 1281 return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, nil) 1282 } 1283 1284 func (s *snapmgrTestSuite) TestRevertRestoresConfigSnapshot(c *C) { 1285 s.state.Lock() 1286 defer s.state.Unlock() 1287 1288 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1289 Active: true, 1290 Sequence: []*snap.SideInfo{ 1291 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 1292 {RealName: "some-snap", Revision: snap.R(2)}, 1293 }, 1294 Current: snap.R(2), 1295 SnapType: "app", 1296 }) 1297 1298 // set configuration for current snap 1299 tr := config.NewTransaction(s.state) 1300 tr.Set("some-snap", "foo", "100") 1301 tr.Commit() 1302 1303 // make config snapshot for rev.1 1304 config.SaveRevisionConfig(s.state, "some-snap", snap.R(1)) 1305 1306 // modify for rev. 2 1307 tr = config.NewTransaction(s.state) 1308 tr.Set("some-snap", "foo", "200") 1309 tr.Commit() 1310 1311 chg := s.state.NewChange("revert", "revert snap") 1312 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 1313 c.Assert(err, IsNil) 1314 chg.AddAll(ts) 1315 1316 s.state.Unlock() 1317 defer s.se.Stop() 1318 s.settle(c) 1319 1320 s.state.Lock() 1321 // config snapshot of rev. 2 has been made by 'revert' 1322 var cfgs map[string]interface{} 1323 c.Assert(s.state.Get("revision-config", &cfgs), IsNil) 1324 c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{ 1325 "1": map[string]interface{}{"foo": "100"}, 1326 "2": map[string]interface{}{"foo": "200"}, 1327 }) 1328 1329 // current snap configuration has been restored from rev. 1 config snapshot 1330 tr = config.NewTransaction(s.state) 1331 var res string 1332 c.Assert(tr.Get("some-snap", "foo", &res), IsNil) 1333 c.Assert(res, Equals, "100") 1334 } 1335 1336 func (s *snapmgrTestSuite) TestRevertNoRevertAgain(c *C) { 1337 siNew := snap.SideInfo{ 1338 RealName: "some-snap", 1339 Revision: snap.R(77), 1340 } 1341 1342 si := snap.SideInfo{ 1343 RealName: "some-snap", 1344 Revision: snap.R(7), 1345 } 1346 1347 s.state.Lock() 1348 defer s.state.Unlock() 1349 1350 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1351 Active: true, 1352 Sequence: []*snap.SideInfo{&si, &siNew}, 1353 Current: snap.R(7), 1354 }) 1355 1356 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 1357 c.Assert(err, ErrorMatches, "no revision to revert to") 1358 c.Assert(ts, IsNil) 1359 } 1360 1361 func (s *snapmgrTestSuite) TestRevertNothingToRevertTo(c *C) { 1362 si := snap.SideInfo{ 1363 RealName: "some-snap", 1364 Revision: snap.R(7), 1365 } 1366 1367 s.state.Lock() 1368 defer s.state.Unlock() 1369 1370 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1371 Active: true, 1372 Sequence: []*snap.SideInfo{&si}, 1373 Current: si.Revision, 1374 }) 1375 1376 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 1377 c.Assert(err, ErrorMatches, "no revision to revert to") 1378 c.Assert(ts, IsNil) 1379 } 1380 1381 func (s *snapmgrTestSuite) TestRevertToRevisionNoValidVersion(c *C) { 1382 si := snap.SideInfo{ 1383 RealName: "some-snap", 1384 Revision: snap.R(7), 1385 } 1386 si2 := snap.SideInfo{ 1387 RealName: "some-snap", 1388 Revision: snap.R(77), 1389 } 1390 1391 s.state.Lock() 1392 defer s.state.Unlock() 1393 1394 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1395 Active: true, 1396 Sequence: []*snap.SideInfo{&si, &si2}, 1397 Current: snap.R(77), 1398 }) 1399 1400 ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("99"), snapstate.Flags{}) 1401 c.Assert(err, ErrorMatches, `cannot find revision 99 for snap "some-snap"`) 1402 c.Assert(ts, IsNil) 1403 } 1404 1405 func (s *snapmgrTestSuite) TestRevertToRevisionAlreadyCurrent(c *C) { 1406 si := snap.SideInfo{ 1407 RealName: "some-snap", 1408 Revision: snap.R(7), 1409 } 1410 si2 := snap.SideInfo{ 1411 RealName: "some-snap", 1412 Revision: snap.R(77), 1413 } 1414 1415 s.state.Lock() 1416 defer s.state.Unlock() 1417 1418 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1419 Active: true, 1420 Sequence: []*snap.SideInfo{&si, &si2}, 1421 Current: snap.R(77), 1422 }) 1423 1424 ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("77"), snapstate.Flags{}) 1425 c.Assert(err, ErrorMatches, `already on requested revision`) 1426 c.Assert(ts, IsNil) 1427 } 1428 1429 func (s *snapmgrTestSuite) TestRevertRunThrough(c *C) { 1430 si := snap.SideInfo{ 1431 RealName: "some-snap", 1432 Revision: snap.R(7), 1433 } 1434 siOld := snap.SideInfo{ 1435 RealName: "some-snap", 1436 Revision: snap.R(2), 1437 } 1438 1439 s.state.Lock() 1440 defer s.state.Unlock() 1441 1442 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1443 Active: true, 1444 SnapType: "app", 1445 Sequence: []*snap.SideInfo{&siOld, &si}, 1446 Current: si.Revision, 1447 }) 1448 1449 chg := s.state.NewChange("revert", "revert a snap backwards") 1450 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 1451 c.Assert(err, IsNil) 1452 chg.AddAll(ts) 1453 1454 s.state.Unlock() 1455 defer s.se.Stop() 1456 s.settle(c) 1457 s.state.Lock() 1458 1459 expected := fakeOps{ 1460 { 1461 op: "remove-snap-aliases", 1462 name: "some-snap", 1463 }, 1464 { 1465 op: "unlink-snap", 1466 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 1467 }, 1468 { 1469 op: "setup-profiles:Doing", 1470 name: "some-snap", 1471 revno: snap.R(2), 1472 }, 1473 { 1474 op: "candidate", 1475 sinfo: snap.SideInfo{ 1476 RealName: "some-snap", 1477 Revision: snap.R(2), 1478 }, 1479 }, 1480 { 1481 op: "link-snap", 1482 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 1483 }, 1484 { 1485 op: "auto-connect:Doing", 1486 name: "some-snap", 1487 revno: snap.R(2), 1488 }, 1489 { 1490 op: "update-aliases", 1491 }, 1492 } 1493 // start with an easier-to-read error if this fails: 1494 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1495 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 1496 1497 // verify that the R(2) version is active now and R(7) is still there 1498 var snapst snapstate.SnapState 1499 err = snapstate.Get(s.state, "some-snap", &snapst) 1500 c.Assert(err, IsNil) 1501 1502 // last refresh time shouldn't be modified on revert. 1503 c.Check(snapst.LastRefreshTime, IsNil) 1504 c.Assert(snapst.Active, Equals, true) 1505 c.Assert(snapst.Current, Equals, snap.R(2)) 1506 c.Assert(snapst.Sequence, HasLen, 2) 1507 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 1508 RealName: "some-snap", 1509 Channel: "", 1510 Revision: snap.R(2), 1511 }) 1512 c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{ 1513 RealName: "some-snap", 1514 Channel: "", 1515 Revision: snap.R(7), 1516 }) 1517 c.Assert(snapst.Block(), DeepEquals, []snap.Revision{snap.R(7)}) 1518 } 1519 1520 func (s *snapmgrTestSuite) TestRevertWithBaseRunThrough(c *C) { 1521 si := snap.SideInfo{ 1522 RealName: "some-snap-with-base", 1523 Revision: snap.R(7), 1524 } 1525 siOld := snap.SideInfo{ 1526 RealName: "some-snap-with-base", 1527 Revision: snap.R(2), 1528 } 1529 1530 s.state.Lock() 1531 defer s.state.Unlock() 1532 1533 // core18 with snapd, no core snap 1534 snapstate.Set(s.state, "core", nil) 1535 snapstate.Set(s.state, "core18", &snapstate.SnapState{ 1536 Active: true, 1537 Sequence: []*snap.SideInfo{ 1538 {RealName: "core18", SnapID: "core18-snap-id", Revision: snap.R(1)}, 1539 }, 1540 Current: snap.R(1), 1541 SnapType: "base", 1542 }) 1543 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 1544 Active: true, 1545 Sequence: []*snap.SideInfo{ 1546 {RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)}, 1547 }, 1548 Current: snap.R(1), 1549 SnapType: "app", 1550 }) 1551 1552 // test snap to revert 1553 snapstate.Set(s.state, "some-snap-with-base", &snapstate.SnapState{ 1554 Active: true, 1555 SnapType: "app", 1556 Sequence: []*snap.SideInfo{&siOld, &si}, 1557 Current: si.Revision, 1558 }) 1559 1560 chg := s.state.NewChange("revert", "revert a snap backwards") 1561 ts, err := snapstate.Revert(s.state, "some-snap-with-base", snapstate.Flags{}) 1562 c.Assert(err, IsNil) 1563 chg.AddAll(ts) 1564 1565 s.state.Unlock() 1566 defer s.se.Stop() 1567 s.settle(c) 1568 s.state.Lock() 1569 1570 expected := fakeOps{ 1571 { 1572 op: "remove-snap-aliases", 1573 name: "some-snap-with-base", 1574 }, 1575 { 1576 op: "unlink-snap", 1577 path: filepath.Join(dirs.SnapMountDir, "some-snap-with-base/7"), 1578 }, 1579 { 1580 op: "setup-profiles:Doing", 1581 name: "some-snap-with-base", 1582 revno: snap.R(2), 1583 }, 1584 { 1585 op: "candidate", 1586 sinfo: snap.SideInfo{ 1587 RealName: "some-snap-with-base", 1588 Revision: snap.R(2), 1589 }, 1590 }, 1591 { 1592 op: "link-snap", 1593 path: filepath.Join(dirs.SnapMountDir, "some-snap-with-base/2"), 1594 }, 1595 { 1596 op: "auto-connect:Doing", 1597 name: "some-snap-with-base", 1598 revno: snap.R(2), 1599 }, 1600 { 1601 op: "update-aliases", 1602 }, 1603 } 1604 // start with an easier-to-read error if this fails: 1605 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1606 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 1607 1608 // verify that the R(2) version is active now and R(7) is still there 1609 var snapst snapstate.SnapState 1610 err = snapstate.Get(s.state, "some-snap-with-base", &snapst) 1611 c.Assert(err, IsNil) 1612 1613 c.Assert(snapst.Active, Equals, true) 1614 c.Assert(snapst.Current, Equals, snap.R(2)) 1615 } 1616 1617 func (s *snapmgrTestSuite) TestParallelInstanceRevertRunThrough(c *C) { 1618 si := snap.SideInfo{ 1619 RealName: "some-snap", 1620 Revision: snap.R(7), 1621 } 1622 siOld := snap.SideInfo{ 1623 RealName: "some-snap", 1624 Revision: snap.R(2), 1625 } 1626 1627 s.state.Lock() 1628 defer s.state.Unlock() 1629 1630 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 1631 Active: true, 1632 SnapType: "app", 1633 Sequence: []*snap.SideInfo{&siOld, &si}, 1634 Current: si.Revision, 1635 InstanceKey: "instance", 1636 }) 1637 1638 // another snap withouth instance key 1639 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1640 Active: true, 1641 SnapType: "app", 1642 Sequence: []*snap.SideInfo{&siOld, &si}, 1643 Current: si.Revision, 1644 }) 1645 1646 chg := s.state.NewChange("revert", "revert a snap backwards") 1647 ts, err := snapstate.Revert(s.state, "some-snap_instance", snapstate.Flags{}) 1648 c.Assert(err, IsNil) 1649 chg.AddAll(ts) 1650 1651 s.state.Unlock() 1652 defer s.se.Stop() 1653 s.settle(c) 1654 s.state.Lock() 1655 1656 expected := fakeOps{ 1657 { 1658 op: "remove-snap-aliases", 1659 name: "some-snap_instance", 1660 }, 1661 { 1662 op: "unlink-snap", 1663 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 1664 }, 1665 { 1666 op: "setup-profiles:Doing", 1667 name: "some-snap_instance", 1668 revno: snap.R(2), 1669 }, 1670 { 1671 op: "candidate", 1672 sinfo: snap.SideInfo{ 1673 RealName: "some-snap", 1674 Revision: snap.R(2), 1675 }, 1676 }, 1677 { 1678 op: "link-snap", 1679 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/2"), 1680 }, 1681 { 1682 op: "auto-connect:Doing", 1683 name: "some-snap_instance", 1684 revno: snap.R(2), 1685 }, 1686 { 1687 op: "update-aliases", 1688 }, 1689 } 1690 // start with an easier-to-read error if this fails: 1691 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1692 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 1693 1694 // verify that the R(2) version is active now and R(7) is still there 1695 var snapst snapstate.SnapState 1696 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 1697 c.Assert(err, IsNil) 1698 1699 c.Assert(snapst.Active, Equals, true) 1700 c.Assert(snapst.Current, Equals, snap.R(2)) 1701 c.Assert(snapst.InstanceKey, Equals, "instance") 1702 c.Assert(snapst.Sequence, HasLen, 2) 1703 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 1704 RealName: "some-snap", 1705 Channel: "", 1706 Revision: snap.R(2), 1707 }) 1708 c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{ 1709 RealName: "some-snap", 1710 Channel: "", 1711 Revision: snap.R(7), 1712 }) 1713 c.Assert(snapst.Block(), DeepEquals, []snap.Revision{snap.R(7)}) 1714 1715 // non instance snap is not affected 1716 var nonInstanceSnapst snapstate.SnapState 1717 err = snapstate.Get(s.state, "some-snap", &nonInstanceSnapst) 1718 c.Assert(err, IsNil) 1719 c.Assert(nonInstanceSnapst.Current, Equals, snap.R(7)) 1720 1721 } 1722 1723 func (s *snapmgrTestSuite) TestRevertWithLocalRevisionRunThrough(c *C) { 1724 si := snap.SideInfo{ 1725 RealName: "some-snap", 1726 Revision: snap.R(-7), 1727 } 1728 siOld := snap.SideInfo{ 1729 RealName: "some-snap", 1730 Revision: snap.R(-2), 1731 } 1732 1733 s.state.Lock() 1734 defer s.state.Unlock() 1735 1736 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1737 Active: true, 1738 SnapType: "app", 1739 Sequence: []*snap.SideInfo{&siOld, &si}, 1740 Current: si.Revision, 1741 }) 1742 1743 chg := s.state.NewChange("revert", "revert a snap backwards") 1744 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 1745 c.Assert(err, IsNil) 1746 chg.AddAll(ts) 1747 1748 s.state.Unlock() 1749 defer s.se.Stop() 1750 s.settle(c) 1751 s.state.Lock() 1752 1753 c.Assert(s.fakeBackend.ops.Ops(), HasLen, 7) 1754 1755 // verify that LocalRevision is still -7 1756 var snapst snapstate.SnapState 1757 err = snapstate.Get(s.state, "some-snap", &snapst) 1758 c.Assert(err, IsNil) 1759 1760 c.Assert(snapst.LocalRevision(), Equals, snap.R(-7)) 1761 } 1762 1763 func (s *snapmgrTestSuite) TestRevertToRevisionNewVersion(c *C) { 1764 siNew := snap.SideInfo{ 1765 RealName: "some-snap", 1766 Revision: snap.R(7), 1767 SnapID: "october", 1768 } 1769 1770 si := snap.SideInfo{ 1771 RealName: "some-snap", 1772 Revision: snap.R(2), 1773 SnapID: "october", 1774 } 1775 1776 s.state.Lock() 1777 defer s.state.Unlock() 1778 1779 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1780 Active: true, 1781 SnapType: "app", 1782 Sequence: []*snap.SideInfo{&si, &siNew}, 1783 Current: snap.R(2), 1784 TrackingChannel: "latest/edge", 1785 }) 1786 1787 chg := s.state.NewChange("revert", "revert a snap forward") 1788 ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(7), snapstate.Flags{}) 1789 c.Assert(err, IsNil) 1790 chg.AddAll(ts) 1791 1792 s.state.Unlock() 1793 defer s.se.Stop() 1794 s.settle(c) 1795 s.state.Lock() 1796 1797 expected := fakeOps{ 1798 { 1799 op: "remove-snap-aliases", 1800 name: "some-snap", 1801 }, 1802 { 1803 op: "unlink-snap", 1804 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 1805 }, 1806 { 1807 op: "setup-profiles:Doing", 1808 name: "some-snap", 1809 revno: snap.R(7), 1810 }, 1811 { 1812 op: "candidate", 1813 sinfo: siNew, 1814 }, 1815 { 1816 op: "link-snap", 1817 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 1818 }, 1819 { 1820 op: "auto-connect:Doing", 1821 name: "some-snap", 1822 revno: snap.R(7), 1823 }, 1824 { 1825 op: "update-aliases", 1826 }, 1827 } 1828 // start with an easier-to-read error if this fails: 1829 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1830 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 1831 1832 // verify that the R(7) version is active now 1833 var snapst snapstate.SnapState 1834 err = snapstate.Get(s.state, "some-snap", &snapst) 1835 c.Assert(err, IsNil) 1836 1837 c.Check(snapst.Active, Equals, true) 1838 c.Check(snapst.Current, Equals, snap.R(7)) 1839 c.Check(snapst.Sequence, HasLen, 2) 1840 c.Check(snapst.TrackingChannel, Equals, "latest/edge") 1841 c.Check(snapst.CurrentSideInfo(), DeepEquals, &siNew) 1842 1843 c.Check(snapst.Block(), HasLen, 0) 1844 } 1845 1846 func (s *snapmgrTestSuite) TestRevertTotalUndoRunThrough(c *C) { 1847 si := snap.SideInfo{ 1848 RealName: "some-snap", 1849 Revision: snap.R(1), 1850 } 1851 si2 := snap.SideInfo{ 1852 RealName: "some-snap", 1853 Revision: snap.R(2), 1854 } 1855 1856 s.state.Lock() 1857 defer s.state.Unlock() 1858 1859 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1860 Active: true, 1861 SnapType: "app", 1862 Sequence: []*snap.SideInfo{&si, &si2}, 1863 Current: si2.Revision, 1864 }) 1865 1866 chg := s.state.NewChange("revert", "revert a snap") 1867 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 1868 c.Assert(err, IsNil) 1869 chg.AddAll(ts) 1870 1871 tasks := ts.Tasks() 1872 last := tasks[len(tasks)-1] 1873 1874 terr := s.state.NewTask("error-trigger", "provoking total undo") 1875 terr.WaitFor(last) 1876 chg.AddTask(terr) 1877 1878 s.state.Unlock() 1879 defer s.se.Stop() 1880 s.settle(c) 1881 s.state.Lock() 1882 1883 expected := fakeOps{ 1884 { 1885 op: "remove-snap-aliases", 1886 name: "some-snap", 1887 }, 1888 { 1889 op: "unlink-snap", 1890 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 1891 }, 1892 { 1893 op: "setup-profiles:Doing", 1894 name: "some-snap", 1895 revno: snap.R(1), 1896 }, 1897 { 1898 op: "candidate", 1899 sinfo: snap.SideInfo{ 1900 RealName: "some-snap", 1901 Revision: snap.R(1), 1902 }, 1903 }, 1904 { 1905 op: "link-snap", 1906 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 1907 }, 1908 { 1909 op: "auto-connect:Doing", 1910 name: "some-snap", 1911 revno: snap.R(1), 1912 }, 1913 { 1914 op: "update-aliases", 1915 }, 1916 // undoing everything from here down... 1917 { 1918 op: "remove-snap-aliases", 1919 name: "some-snap", 1920 }, 1921 { 1922 op: "unlink-snap", 1923 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 1924 }, 1925 { 1926 op: "setup-profiles:Undoing", 1927 name: "some-snap", 1928 revno: snap.R(1), 1929 }, 1930 { 1931 op: "link-snap", 1932 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 1933 }, 1934 { 1935 op: "update-aliases", 1936 }, 1937 } 1938 // start with an easier-to-read error if this fails: 1939 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1940 c.Check(s.fakeBackend.ops, DeepEquals, expected) 1941 1942 // verify snaps in the system state 1943 var snapst snapstate.SnapState 1944 err = snapstate.Get(s.state, "some-snap", &snapst) 1945 c.Assert(err, IsNil) 1946 1947 c.Assert(snapst.Active, Equals, true) 1948 c.Assert(snapst.Sequence, HasLen, 2) 1949 c.Assert(snapst.Current, Equals, si2.Revision) 1950 } 1951 1952 func (s *snapmgrTestSuite) TestRevertUndoRunThrough(c *C) { 1953 si := snap.SideInfo{ 1954 RealName: "some-snap", 1955 Revision: snap.R(1), 1956 } 1957 si2 := snap.SideInfo{ 1958 RealName: "some-snap", 1959 Revision: snap.R(2), 1960 } 1961 1962 s.state.Lock() 1963 defer s.state.Unlock() 1964 1965 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1966 Active: true, 1967 SnapType: "app", 1968 Sequence: []*snap.SideInfo{&si, &si2}, 1969 Current: si2.Revision, 1970 }) 1971 1972 chg := s.state.NewChange("revert", "install a revert") 1973 ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{}) 1974 c.Assert(err, IsNil) 1975 chg.AddAll(ts) 1976 1977 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/1") 1978 1979 s.state.Unlock() 1980 defer s.se.Stop() 1981 s.settle(c) 1982 s.state.Lock() 1983 1984 expected := fakeOps{ 1985 { 1986 op: "remove-snap-aliases", 1987 name: "some-snap", 1988 }, 1989 { 1990 op: "unlink-snap", 1991 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 1992 }, 1993 { 1994 op: "setup-profiles:Doing", 1995 name: "some-snap", 1996 revno: snap.R(1), 1997 }, 1998 { 1999 op: "candidate", 2000 sinfo: snap.SideInfo{ 2001 RealName: "some-snap", 2002 Revision: snap.R(1), 2003 }, 2004 }, 2005 { 2006 op: "link-snap.failed", 2007 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 2008 }, 2009 // undo stuff here 2010 { 2011 op: "unlink-snap", 2012 path: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 2013 }, 2014 { 2015 op: "setup-profiles:Undoing", 2016 name: "some-snap", 2017 revno: snap.R(1), 2018 }, 2019 { 2020 op: "link-snap", 2021 path: filepath.Join(dirs.SnapMountDir, "some-snap/2"), 2022 }, 2023 { 2024 op: "update-aliases", 2025 }, 2026 } 2027 2028 // ensure all our tasks ran 2029 // start with an easier-to-read error if this fails: 2030 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2031 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 2032 2033 // verify snaps in the system state 2034 var snapst snapstate.SnapState 2035 err = snapstate.Get(s.state, "some-snap", &snapst) 2036 c.Assert(err, IsNil) 2037 2038 c.Assert(snapst.Active, Equals, true) 2039 c.Assert(snapst.Sequence, HasLen, 2) 2040 c.Assert(snapst.Current, Equals, snap.R(2)) 2041 } 2042 2043 func (s *snapmgrTestSuite) TestEnableDoesNotEnableAgain(c *C) { 2044 si := snap.SideInfo{ 2045 RealName: "some-snap", 2046 Revision: snap.R(7), 2047 } 2048 2049 s.state.Lock() 2050 defer s.state.Unlock() 2051 2052 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2053 Sequence: []*snap.SideInfo{&si}, 2054 Current: snap.R(7), 2055 Active: true, 2056 }) 2057 2058 ts, err := snapstate.Enable(s.state, "some-snap") 2059 c.Assert(err, ErrorMatches, `snap "some-snap" already enabled`) 2060 c.Assert(ts, IsNil) 2061 } 2062 2063 func (s *snapmgrTestSuite) TestEnableRunThrough(c *C) { 2064 si := snap.SideInfo{ 2065 RealName: "some-snap", 2066 Revision: snap.R(7), 2067 Channel: "edge", 2068 SnapID: "foo", 2069 } 2070 2071 s.state.Lock() 2072 defer s.state.Unlock() 2073 2074 flags := snapstate.Flags{ 2075 DevMode: true, 2076 JailMode: true, 2077 Classic: true, 2078 TryMode: true, 2079 Required: true, 2080 } 2081 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2082 Sequence: []*snap.SideInfo{&si}, 2083 Current: si.Revision, 2084 Active: false, 2085 TrackingChannel: "latest/edge", 2086 Flags: flags, 2087 AliasesPending: true, 2088 AutoAliasesDisabled: true, 2089 }) 2090 2091 chg := s.state.NewChange("enable", "enable a snap") 2092 ts, err := snapstate.Enable(s.state, "some-snap") 2093 c.Assert(err, IsNil) 2094 chg.AddAll(ts) 2095 2096 s.state.Unlock() 2097 defer s.se.Stop() 2098 s.settle(c) 2099 s.state.Lock() 2100 2101 expected := fakeOps{ 2102 { 2103 op: "setup-profiles:Doing", 2104 name: "some-snap", 2105 revno: snap.R(7), 2106 }, 2107 { 2108 op: "candidate", 2109 sinfo: si, 2110 }, 2111 { 2112 op: "link-snap", 2113 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 2114 }, 2115 { 2116 op: "auto-connect:Doing", 2117 name: "some-snap", 2118 revno: snap.R(7), 2119 }, 2120 { 2121 op: "update-aliases", 2122 }, 2123 } 2124 // start with an easier-to-read error if this fails: 2125 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2126 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 2127 2128 var snapst snapstate.SnapState 2129 err = snapstate.Get(s.state, "some-snap", &snapst) 2130 c.Assert(err, IsNil) 2131 c.Check(snapst.Flags, DeepEquals, flags) 2132 2133 c.Assert(snapst.Active, Equals, true) 2134 c.Assert(snapst.AliasesPending, Equals, false) 2135 c.Assert(snapst.AutoAliasesDisabled, Equals, true) 2136 2137 info, err := snapst.CurrentInfo() 2138 c.Assert(err, IsNil) 2139 c.Assert(info.Channel, Equals, "edge") 2140 c.Assert(info.SnapID, Equals, "foo") 2141 2142 first := ts.Tasks()[0] 2143 snapsup, err := snapstate.TaskSnapSetup(first) 2144 c.Assert(err, IsNil) 2145 c.Check(snapsup, DeepEquals, &snapstate.SnapSetup{ 2146 SideInfo: &si, 2147 Flags: flags, 2148 Type: snap.TypeApp, 2149 PlugsOnly: true, 2150 }) 2151 } 2152 2153 func (s *snapmgrTestSuite) TestDisableRunThrough(c *C) { 2154 si := snap.SideInfo{ 2155 RealName: "some-snap", 2156 Revision: snap.R(7), 2157 } 2158 2159 s.state.Lock() 2160 defer s.state.Unlock() 2161 2162 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2163 Sequence: []*snap.SideInfo{&si}, 2164 Current: si.Revision, 2165 Active: true, 2166 SnapType: "app", 2167 }) 2168 2169 chg := s.state.NewChange("disable", "disable a snap") 2170 ts, err := snapstate.Disable(s.state, "some-snap") 2171 c.Assert(err, IsNil) 2172 chg.AddAll(ts) 2173 2174 s.state.Unlock() 2175 defer s.se.Stop() 2176 s.settle(c) 2177 s.state.Lock() 2178 2179 expected := fakeOps{ 2180 { 2181 op: "remove-snap-aliases", 2182 name: "some-snap", 2183 }, 2184 { 2185 op: "unlink-snap", 2186 path: filepath.Join(dirs.SnapMountDir, "some-snap/7"), 2187 }, 2188 { 2189 op: "remove-profiles:Doing", 2190 name: "some-snap", 2191 revno: snap.R(7), 2192 }, 2193 } 2194 // start with an easier-to-read error if this fails: 2195 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2196 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 2197 2198 var snapst snapstate.SnapState 2199 err = snapstate.Get(s.state, "some-snap", &snapst) 2200 c.Assert(err, IsNil) 2201 2202 c.Assert(snapst.Active, Equals, false) 2203 c.Assert(snapst.AliasesPending, Equals, true) 2204 2205 first := ts.Tasks()[0] 2206 snapsup, err := snapstate.TaskSnapSetup(first) 2207 c.Assert(err, IsNil) 2208 c.Check(snapsup, DeepEquals, &snapstate.SnapSetup{ 2209 SideInfo: &snap.SideInfo{ 2210 RealName: "some-snap", 2211 Revision: snap.R(7), 2212 }, 2213 Type: snap.TypeApp, 2214 PlugsOnly: true, 2215 }) 2216 } 2217 2218 func (s *snapmgrTestSuite) TestParallelInstanceEnableRunThrough(c *C) { 2219 si := snap.SideInfo{ 2220 RealName: "some-snap", 2221 Revision: snap.R(7), 2222 Channel: "edge", 2223 SnapID: "foo", 2224 } 2225 2226 s.state.Lock() 2227 defer s.state.Unlock() 2228 2229 flags := snapstate.Flags{ 2230 DevMode: true, 2231 JailMode: true, 2232 Classic: true, 2233 TryMode: true, 2234 Required: true, 2235 } 2236 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 2237 Sequence: []*snap.SideInfo{&si}, 2238 Current: si.Revision, 2239 Active: false, 2240 TrackingChannel: "latest/edge", 2241 Flags: flags, 2242 AliasesPending: true, 2243 AutoAliasesDisabled: true, 2244 InstanceKey: "instance", 2245 }) 2246 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2247 Sequence: []*snap.SideInfo{&si}, 2248 Current: si.Revision, 2249 Active: false, 2250 TrackingChannel: "latest/edge", 2251 Flags: flags, 2252 AliasesPending: true, 2253 AutoAliasesDisabled: true, 2254 }) 2255 2256 chg := s.state.NewChange("enable", "enable a snap") 2257 ts, err := snapstate.Enable(s.state, "some-snap_instance") 2258 c.Assert(err, IsNil) 2259 chg.AddAll(ts) 2260 2261 s.state.Unlock() 2262 s.settle(c) 2263 s.state.Lock() 2264 2265 expected := fakeOps{ 2266 { 2267 op: "setup-profiles:Doing", 2268 name: "some-snap_instance", 2269 revno: snap.R(7), 2270 }, 2271 { 2272 op: "candidate", 2273 sinfo: si, 2274 }, 2275 { 2276 op: "link-snap", 2277 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 2278 }, 2279 { 2280 op: "auto-connect:Doing", 2281 name: "some-snap_instance", 2282 revno: snap.R(7), 2283 }, 2284 { 2285 op: "update-aliases", 2286 }, 2287 } 2288 // start with an easier-to-read error if this fails: 2289 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2290 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 2291 2292 var snapst snapstate.SnapState 2293 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 2294 c.Assert(err, IsNil) 2295 c.Check(snapst.Flags, DeepEquals, flags) 2296 2297 c.Assert(snapst.InstanceKey, Equals, "instance") 2298 c.Assert(snapst.Active, Equals, true) 2299 c.Assert(snapst.AliasesPending, Equals, false) 2300 c.Assert(snapst.AutoAliasesDisabled, Equals, true) 2301 2302 info, err := snapst.CurrentInfo() 2303 c.Assert(err, IsNil) 2304 c.Assert(info.Channel, Equals, "edge") 2305 c.Assert(info.SnapID, Equals, "foo") 2306 2307 // the non-parallel instance is still disabled 2308 snapst = snapstate.SnapState{} 2309 err = snapstate.Get(s.state, "some-snap", &snapst) 2310 c.Assert(err, IsNil) 2311 c.Assert(snapst.InstanceKey, Equals, "") 2312 c.Assert(snapst.Active, Equals, false) 2313 } 2314 2315 func (s *snapmgrTestSuite) TestParallelInstanceDisableRunThrough(c *C) { 2316 si := snap.SideInfo{ 2317 RealName: "some-snap", 2318 Revision: snap.R(7), 2319 } 2320 2321 s.state.Lock() 2322 defer s.state.Unlock() 2323 2324 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2325 Sequence: []*snap.SideInfo{&si}, 2326 Current: si.Revision, 2327 Active: true, 2328 }) 2329 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 2330 Sequence: []*snap.SideInfo{&si}, 2331 Current: si.Revision, 2332 Active: true, 2333 InstanceKey: "instance", 2334 }) 2335 2336 chg := s.state.NewChange("disable", "disable a snap") 2337 ts, err := snapstate.Disable(s.state, "some-snap_instance") 2338 c.Assert(err, IsNil) 2339 chg.AddAll(ts) 2340 2341 s.state.Unlock() 2342 s.settle(c) 2343 s.state.Lock() 2344 2345 expected := fakeOps{ 2346 { 2347 op: "remove-snap-aliases", 2348 name: "some-snap_instance", 2349 }, 2350 { 2351 op: "unlink-snap", 2352 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"), 2353 }, 2354 { 2355 op: "remove-profiles:Doing", 2356 name: "some-snap_instance", 2357 revno: snap.R(7), 2358 }, 2359 } 2360 // start with an easier-to-read error if this fails: 2361 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2362 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 2363 2364 var snapst snapstate.SnapState 2365 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 2366 c.Assert(err, IsNil) 2367 c.Assert(snapst.InstanceKey, Equals, "instance") 2368 c.Assert(snapst.Active, Equals, false) 2369 c.Assert(snapst.AliasesPending, Equals, true) 2370 2371 // the non-parallel instance is still active 2372 snapst = snapstate.SnapState{} 2373 err = snapstate.Get(s.state, "some-snap", &snapst) 2374 c.Assert(err, IsNil) 2375 c.Assert(snapst.InstanceKey, Equals, "") 2376 c.Assert(snapst.Active, Equals, true) 2377 } 2378 2379 type switchScenario struct { 2380 chanFrom string 2381 chanTo string 2382 cohFrom string 2383 cohTo string 2384 summary string 2385 } 2386 2387 var switchScenarios = map[string]switchScenario{ 2388 "no cohort at all": { 2389 chanFrom: "latest/stable", 2390 chanTo: "some-channel/stable", 2391 cohFrom: "", 2392 cohTo: "", 2393 summary: `Switch snap "some-snap" from channel "latest/stable" to "some-channel/stable"`, 2394 }, 2395 "no cohort, from empty channel": { 2396 chanFrom: "", 2397 chanTo: "some-channel/stable", 2398 cohFrom: "", 2399 cohTo: "", 2400 summary: `Switch snap "some-snap" to channel "some-channel/stable"`, 2401 }, 2402 "no cohort change requested": { 2403 chanFrom: "latest/stable", 2404 chanTo: "some-channel/stable", 2405 cohFrom: "some-cohort", 2406 cohTo: "some-cohort", 2407 summary: `Switch snap "some-snap" from channel "latest/stable" to "some-channel/stable"`, 2408 }, 2409 "leave cohort": { 2410 chanFrom: "latest/stable", 2411 chanTo: "latest/stable", 2412 cohFrom: "some-cohort", 2413 cohTo: "", 2414 summary: `Switch snap "some-snap" away from cohort "…me-cohort"`, 2415 }, 2416 "leave cohort, change channel": { 2417 chanFrom: "latest/stable", 2418 chanTo: "latest/edge", 2419 cohFrom: "some-cohort", 2420 cohTo: "", 2421 summary: `Switch snap "some-snap" from channel "latest/stable" to "latest/edge" and away from cohort "…me-cohort"`, 2422 }, 2423 "leave cohort, change from empty channel": { 2424 chanFrom: "", 2425 chanTo: "latest/stable", 2426 cohFrom: "some-cohort", 2427 cohTo: "", 2428 summary: `Switch snap "some-snap" to channel "latest/stable" and away from cohort "…me-cohort"`, 2429 }, 2430 "no channel at all": { 2431 chanFrom: "", 2432 chanTo: "", 2433 cohFrom: "some-cohort", 2434 cohTo: "some-other-cohort", 2435 summary: `Switch snap "some-snap" from cohort "…me-cohort" to "…er-cohort"`, 2436 }, 2437 "no channel change requested": { 2438 chanFrom: "latest/stable", 2439 chanTo: "latest/stable", 2440 cohFrom: "some-cohort", 2441 cohTo: "some-other-cohort", 2442 summary: `Switch snap "some-snap" from cohort "…me-cohort" to "…er-cohort"`, 2443 }, 2444 "no channel change requested, from empty cohort": { 2445 chanFrom: "latest/stable", 2446 chanTo: "latest/stable", 2447 cohFrom: "", 2448 cohTo: "some-cohort", 2449 summary: `Switch snap "some-snap" from no cohort to "…me-cohort"`, 2450 }, 2451 "all change": { 2452 chanFrom: "latest/stable", 2453 chanTo: "latest/edge", 2454 cohFrom: "some-cohort", 2455 cohTo: "some-other-cohort", 2456 summary: `Switch snap "some-snap" from channel "latest/stable" to "latest/edge" and from cohort "…me-cohort" to "…er-cohort"`, 2457 }, 2458 "all change, from empty channel": { 2459 chanFrom: "", 2460 chanTo: "latest/stable", 2461 cohFrom: "some-cohort", 2462 cohTo: "some-other-cohort", 2463 summary: `Switch snap "some-snap" to channel "latest/stable" and from cohort "…me-cohort" to "…er-cohort"`, 2464 }, 2465 "all change, from empty cohort": { 2466 chanFrom: "latest/stable", 2467 chanTo: "latest/edge", 2468 cohFrom: "", 2469 cohTo: "some-cohort", 2470 summary: `Switch snap "some-snap" from channel "latest/stable" to "latest/edge" and from no cohort to "…me-cohort"`, 2471 }, 2472 "all change, from empty channel and cohort": { 2473 chanFrom: "", 2474 chanTo: "latest/stable", 2475 cohFrom: "", 2476 cohTo: "some-cohort", 2477 summary: `Switch snap "some-snap" to channel "latest/stable" and from no cohort to "…me-cohort"`, 2478 }, 2479 "no change": { 2480 chanFrom: "latest/stable", 2481 chanTo: "latest/stable", 2482 cohFrom: "some-cohort", 2483 cohTo: "some-cohort", 2484 summary: `No change switch (no-op)`, 2485 }, 2486 } 2487 2488 func (s *snapmgrTestSuite) TestSwitchScenarios(c *C) { 2489 for k, t := range switchScenarios { 2490 s.testSwitchScenario(c, k, t) 2491 } 2492 } 2493 2494 func (s *snapmgrTestSuite) testSwitchScenario(c *C, desc string, t switchScenario) { 2495 comment := Commentf("%q (%+v)", desc, t) 2496 si := snap.SideInfo{ 2497 RealName: "some-snap", 2498 Revision: snap.R(7), 2499 Channel: t.chanFrom, 2500 SnapID: "foo", 2501 } 2502 2503 s.state.Lock() 2504 defer s.state.Unlock() 2505 2506 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2507 Sequence: []*snap.SideInfo{&si}, 2508 Current: si.Revision, 2509 TrackingChannel: t.chanFrom, 2510 CohortKey: t.cohFrom, 2511 }) 2512 2513 summary := snapstate.SwitchSummary("some-snap", t.chanFrom, t.chanTo, t.cohFrom, t.cohTo) 2514 c.Check(summary, Equals, t.summary, comment) 2515 chg := s.state.NewChange("switch-snap", summary) 2516 ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{ 2517 Channel: t.chanTo, 2518 CohortKey: t.cohTo, 2519 LeaveCohort: t.cohFrom != "" && t.cohTo == "", 2520 }) 2521 c.Assert(err, IsNil, comment) 2522 chg.AddAll(ts) 2523 2524 s.state.Unlock() 2525 s.settle(c) 2526 s.state.Lock() 2527 2528 // switch is not really really doing anything backend related 2529 c.Assert(s.fakeBackend.ops, HasLen, 0, comment) 2530 2531 expectedChanTo := t.chanTo 2532 if t.chanTo == "" { 2533 expectedChanTo = t.chanFrom 2534 } 2535 expectedCohTo := t.cohTo 2536 2537 // ensure the desired channel/cohort has changed 2538 var snapst snapstate.SnapState 2539 err = snapstate.Get(s.state, "some-snap", &snapst) 2540 c.Assert(err, IsNil, comment) 2541 c.Assert(snapst.TrackingChannel, Equals, expectedChanTo, comment) 2542 c.Assert(snapst.CohortKey, Equals, expectedCohTo, comment) 2543 2544 // ensure the current info has not changed 2545 info, err := snapst.CurrentInfo() 2546 c.Assert(err, IsNil, comment) 2547 c.Assert(info.Channel, Equals, t.chanFrom, comment) 2548 } 2549 2550 func (s *snapmgrTestSuite) TestParallelInstallSwitchRunThrough(c *C) { 2551 si := snap.SideInfo{ 2552 RealName: "some-snap", 2553 Revision: snap.R(7), 2554 Channel: "edge", 2555 SnapID: "foo", 2556 } 2557 2558 s.state.Lock() 2559 defer s.state.Unlock() 2560 2561 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2562 Sequence: []*snap.SideInfo{&si}, 2563 Current: si.Revision, 2564 TrackingChannel: "latest/edge", 2565 }) 2566 2567 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 2568 Sequence: []*snap.SideInfo{&si}, 2569 Current: si.Revision, 2570 TrackingChannel: "latest/edge", 2571 InstanceKey: "instance", 2572 }) 2573 2574 chg := s.state.NewChange("switch-snap", "switch snap to some-channel") 2575 ts, err := snapstate.Switch(s.state, "some-snap_instance", &snapstate.RevisionOptions{Channel: "some-channel"}) 2576 c.Assert(err, IsNil) 2577 chg.AddAll(ts) 2578 2579 s.state.Unlock() 2580 defer s.se.Stop() 2581 s.settle(c) 2582 s.state.Lock() 2583 2584 // switch is not really really doing anything backend related 2585 c.Assert(s.fakeBackend.ops, HasLen, 0) 2586 2587 // ensure the desired channel has changed 2588 var snapst snapstate.SnapState 2589 err = snapstate.Get(s.state, "some-snap_instance", &snapst) 2590 c.Assert(err, IsNil) 2591 c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable") 2592 2593 // ensure the current info has not changed 2594 info, err := snapst.CurrentInfo() 2595 c.Assert(err, IsNil) 2596 c.Assert(info.Channel, Equals, "edge") 2597 2598 // Ensure that the non-instance snap is unchanged 2599 var nonInstanceSnapst snapstate.SnapState 2600 err = snapstate.Get(s.state, "some-snap", &nonInstanceSnapst) 2601 c.Assert(err, IsNil) 2602 c.Assert(nonInstanceSnapst.TrackingChannel, Equals, "latest/edge") 2603 } 2604 2605 func (s *snapmgrTestSuite) TestDisableDoesNotEnableAgain(c *C) { 2606 si := snap.SideInfo{ 2607 RealName: "some-snap", 2608 Revision: snap.R(7), 2609 } 2610 2611 s.state.Lock() 2612 defer s.state.Unlock() 2613 2614 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2615 Sequence: []*snap.SideInfo{&si}, 2616 Current: snap.R(7), 2617 Active: false, 2618 }) 2619 2620 ts, err := snapstate.Disable(s.state, "some-snap") 2621 c.Assert(err, ErrorMatches, `snap "some-snap" already disabled`) 2622 c.Assert(ts, IsNil) 2623 } 2624 2625 func (s *snapmgrTestSuite) TestAbortCausesNoErrReport(c *C) { 2626 errReported := 0 2627 restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) { 2628 errReported++ 2629 return "oops-id", nil 2630 }) 2631 defer restore() 2632 2633 s.state.Lock() 2634 defer s.state.Unlock() 2635 2636 chg := s.state.NewChange("install", "install a snap") 2637 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 2638 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2639 c.Assert(err, IsNil) 2640 2641 s.fakeBackend.linkSnapWaitCh = make(chan int) 2642 s.fakeBackend.linkSnapWaitTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11") 2643 go func() { 2644 <-s.fakeBackend.linkSnapWaitCh 2645 chg.Abort() 2646 s.fakeBackend.linkSnapWaitCh <- 1 2647 }() 2648 2649 chg.AddAll(ts) 2650 2651 s.state.Unlock() 2652 defer s.se.Stop() 2653 s.settle(c) 2654 s.state.Lock() 2655 2656 c.Check(chg.Status(), Equals, state.UndoneStatus) 2657 c.Assert(errReported, Equals, 0) 2658 } 2659 2660 func (s *snapmgrTestSuite) TestErrreportDisable(c *C) { 2661 s.state.Lock() 2662 defer s.state.Unlock() 2663 2664 tr := config.NewTransaction(s.state) 2665 tr.Set("core", "problem-reports.disabled", true) 2666 tr.Commit() 2667 2668 restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) { 2669 c.Fatalf("this should not be reached") 2670 return "", nil 2671 }) 2672 defer restore() 2673 2674 chg := s.state.NewChange("install", "install a snap") 2675 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 2676 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2677 c.Assert(err, IsNil) 2678 chg.AddAll(ts) 2679 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11") 2680 2681 s.state.Unlock() 2682 defer s.se.Stop() 2683 s.settle(c) 2684 s.state.Lock() 2685 2686 // no failure report was generated 2687 } 2688 2689 func (s *snapmgrTestSuite) TestEnsureRefreshesAtSeedPolicy(c *C) { 2690 // special policy only on classic 2691 r := release.MockOnClassic(true) 2692 defer r() 2693 // set at not seeded yet 2694 st := s.state 2695 st.Lock() 2696 st.Set("seeded", nil) 2697 st.Unlock() 2698 2699 s.snapmgr.Ensure() 2700 2701 st.Lock() 2702 defer st.Unlock() 2703 2704 // check that refresh policies have run in this case 2705 var t1 time.Time 2706 err := st.Get("last-refresh-hints", &t1) 2707 c.Check(err, IsNil) 2708 tr := config.NewTransaction(st) 2709 err = tr.Get("core", "refresh.hold", &t1) 2710 c.Check(err, IsNil) 2711 } 2712 2713 func (s *snapmgrTestSuite) TestEsnureCleansOldSideloads(c *C) { 2714 filenames := func() []string { 2715 filenames, _ := filepath.Glob(filepath.Join(dirs.SnapBlobDir, "*")) 2716 return filenames 2717 } 2718 2719 defer snapstate.MockLocalInstallCleanupWait(200 * time.Millisecond)() 2720 c.Assert(os.MkdirAll(dirs.SnapBlobDir, 0700), IsNil) 2721 // sanity check; note * in go glob matches .foo 2722 c.Assert(filenames(), HasLen, 0) 2723 2724 s0 := filepath.Join(dirs.SnapBlobDir, "some.snap") 2725 s1 := filepath.Join(dirs.SnapBlobDir, dirs.LocalInstallBlobTempPrefix+"-12345") 2726 s2 := filepath.Join(dirs.SnapBlobDir, dirs.LocalInstallBlobTempPrefix+"-67890") 2727 2728 c.Assert(ioutil.WriteFile(s0, nil, 0600), IsNil) 2729 c.Assert(ioutil.WriteFile(s1, nil, 0600), IsNil) 2730 c.Assert(ioutil.WriteFile(s2, nil, 0600), IsNil) 2731 2732 t1 := time.Now() 2733 t0 := t1.Add(-time.Hour) 2734 2735 c.Assert(os.Chtimes(s0, t0, t0), IsNil) 2736 c.Assert(os.Chtimes(s1, t0, t0), IsNil) 2737 c.Assert(os.Chtimes(s2, t1, t1), IsNil) 2738 2739 // all there 2740 c.Assert(filenames(), DeepEquals, []string{s1, s2, s0}) 2741 2742 // set last cleanup in the future 2743 defer snapstate.MockLocalInstallLastCleanup(t1.Add(time.Minute))() 2744 s.snapmgr.Ensure() 2745 // all there ( -> cleanup not done) 2746 c.Assert(filenames(), DeepEquals, []string{s1, s2, s0}) 2747 2748 // set last cleanup to epoch 2749 snapstate.MockLocalInstallLastCleanup(time.Time{}) 2750 2751 s.snapmgr.Ensure() 2752 // oldest sideload gone 2753 c.Assert(filenames(), DeepEquals, []string{s2, s0}) 2754 2755 time.Sleep(200 * time.Millisecond) 2756 2757 s.snapmgr.Ensure() 2758 // all sideloads gone 2759 c.Assert(filenames(), DeepEquals, []string{s0}) 2760 2761 } 2762 2763 func (s *snapmgrTestSuite) verifyRefreshLast(c *C) { 2764 var lastRefresh time.Time 2765 2766 s.state.Get("last-refresh", &lastRefresh) 2767 c.Check(time.Now().Year(), Equals, lastRefresh.Year()) 2768 } 2769 2770 func makeTestRefreshConfig(st *state.State) { 2771 // avoid special at seed policy 2772 now := time.Now() 2773 st.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, now.Location())) 2774 2775 tr := config.NewTransaction(st) 2776 tr.Set("core", "refresh.timer", "00:00-23:59") 2777 tr.Commit() 2778 } 2779 2780 func (s *snapmgrTestSuite) TestEnsureRefreshRefusesLegacyWeekdaySchedules(c *C) { 2781 s.state.Lock() 2782 defer s.state.Unlock() 2783 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 2784 2785 logbuf, restore := logger.MockLogger() 2786 defer restore() 2787 2788 s.state.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, time.UTC)) 2789 tr := config.NewTransaction(s.state) 2790 tr.Set("core", "refresh.timer", "") 2791 tr.Set("core", "refresh.schedule", "00:00-23:59/mon@12:00-14:00") 2792 tr.Commit() 2793 2794 // Ensure() also runs ensureRefreshes() 2795 s.state.Unlock() 2796 s.se.Ensure() 2797 s.state.Lock() 2798 2799 c.Check(logbuf.String(), testutil.Contains, `cannot use refresh.schedule configuration: cannot parse "mon@12:00": not a valid time`) 2800 schedule, legacy, err := s.snapmgr.RefreshSchedule() 2801 c.Assert(err, IsNil) 2802 c.Check(schedule, Equals, "00:00~24:00/4") 2803 c.Check(legacy, Equals, false) 2804 2805 tr = config.NewTransaction(s.state) 2806 refreshTimer := "canary" 2807 refreshSchedule := "canary" 2808 c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil) 2809 c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil) 2810 c.Check(refreshTimer, Equals, "") 2811 c.Check(refreshSchedule, Equals, "00:00-23:59/mon@12:00-14:00") 2812 } 2813 2814 func (s *snapmgrTestSuite) TestEnsureRefreshLegacyScheduleIsLowerPriority(c *C) { 2815 s.state.Lock() 2816 defer s.state.Unlock() 2817 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 2818 2819 s.state.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, time.UTC)) 2820 tr := config.NewTransaction(s.state) 2821 tr.Set("core", "refresh.timer", "00:00-23:59,,mon,12:00-14:00") 2822 // legacy schedule is invalid 2823 tr.Set("core", "refresh.schedule", "00:00-23:59/mon@12:00-14:00") 2824 tr.Commit() 2825 2826 // Ensure() also runs ensureRefreshes() 2827 s.state.Unlock() 2828 s.se.Ensure() 2829 s.state.Lock() 2830 2831 // expecting new refresh.timer to have been used, fallback to legacy was 2832 // not attempted otherwise it would get reset to the default due to 2833 // refresh.schedule being garbage 2834 schedule, legacy, err := s.snapmgr.RefreshSchedule() 2835 c.Assert(err, IsNil) 2836 c.Check(schedule, Equals, "00:00-23:59,,mon,12:00-14:00") 2837 c.Check(legacy, Equals, false) 2838 } 2839 2840 func (s *snapmgrTestSuite) TestEnsureRefreshFallbackToLegacySchedule(c *C) { 2841 s.state.Lock() 2842 defer s.state.Unlock() 2843 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 2844 2845 tr := config.NewTransaction(s.state) 2846 tr.Set("core", "refresh.timer", "") 2847 tr.Set("core", "refresh.schedule", "00:00-23:59") 2848 tr.Commit() 2849 2850 // Ensure() also runs ensureRefreshes() 2851 s.state.Unlock() 2852 s.se.Ensure() 2853 s.state.Lock() 2854 2855 // refresh.timer is unset, triggering automatic fallback to legacy 2856 // schedule if that was set 2857 schedule, legacy, err := s.snapmgr.RefreshSchedule() 2858 c.Assert(err, IsNil) 2859 c.Check(schedule, Equals, "00:00-23:59") 2860 c.Check(legacy, Equals, true) 2861 } 2862 2863 func (s *snapmgrTestSuite) TestEnsureRefreshFallbackToDefaultOnError(c *C) { 2864 s.state.Lock() 2865 defer s.state.Unlock() 2866 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 2867 2868 tr := config.NewTransaction(s.state) 2869 tr.Set("core", "refresh.timer", "garbage-in") 2870 tr.Set("core", "refresh.schedule", "00:00-23:59") 2871 tr.Commit() 2872 2873 // Ensure() also runs ensureRefreshes() 2874 s.state.Unlock() 2875 s.se.Ensure() 2876 s.state.Lock() 2877 2878 // automatic fallback to default schedule if refresh.timer is set but 2879 // cannot be parsed 2880 schedule, legacy, err := s.snapmgr.RefreshSchedule() 2881 c.Assert(err, IsNil) 2882 c.Check(schedule, Equals, "00:00~24:00/4") 2883 c.Check(legacy, Equals, false) 2884 2885 tr = config.NewTransaction(s.state) 2886 refreshTimer := "canary" 2887 refreshSchedule := "canary" 2888 c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil) 2889 c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil) 2890 c.Check(refreshTimer, Equals, "garbage-in") 2891 c.Check(refreshSchedule, Equals, "00:00-23:59") 2892 } 2893 2894 func (s *snapmgrTestSuite) TestEnsureRefreshFallbackOnEmptyToDefaultSchedule(c *C) { 2895 s.state.Lock() 2896 defer s.state.Unlock() 2897 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 2898 2899 tr := config.NewTransaction(s.state) 2900 tr.Set("core", "refresh.timer", "") 2901 tr.Set("core", "refresh.schedule", "") 2902 tr.Commit() 2903 2904 // Ensure() also runs ensureRefreshes() 2905 s.state.Unlock() 2906 s.se.Ensure() 2907 s.state.Lock() 2908 2909 // automatic fallback to default schedule if neither refresh.timer nor 2910 // refresh.schedule was set 2911 schedule, legacy, err := s.snapmgr.RefreshSchedule() 2912 c.Assert(err, IsNil) 2913 c.Check(schedule, Equals, "00:00~24:00/4") 2914 c.Check(legacy, Equals, false) 2915 2916 tr = config.NewTransaction(s.state) 2917 refreshTimer := "canary" 2918 refreshSchedule := "canary" 2919 c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil) 2920 c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil) 2921 c.Check(refreshTimer, Equals, "") 2922 c.Check(refreshSchedule, Equals, "") 2923 } 2924 2925 func (s *snapmgrTestSuite) TestEnsureRefreshesNoUpdate(c *C) { 2926 s.state.Lock() 2927 defer s.state.Unlock() 2928 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 2929 2930 makeTestRefreshConfig(s.state) 2931 2932 // Ensure() also runs ensureRefreshes() 2933 s.state.Unlock() 2934 s.snapmgr.Ensure() 2935 s.state.Lock() 2936 2937 // nothing needs to be done, but last-refresh got updated 2938 c.Check(s.state.Changes(), HasLen, 0) 2939 s.verifyRefreshLast(c) 2940 2941 // ensure the next-refresh time is reset and re-calculated 2942 c.Check(s.snapmgr.NextRefresh().IsZero(), Equals, true) 2943 } 2944 2945 func (s *snapmgrTestSuite) TestEnsureRefreshesAlreadyRanInThisInterval(c *C) { 2946 s.state.Lock() 2947 defer s.state.Unlock() 2948 2949 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { 2950 return true, nil 2951 } 2952 nextRefresh := s.snapmgr.NextRefresh() 2953 c.Check(nextRefresh.IsZero(), Equals, true) 2954 2955 now := time.Now() 2956 fakeLastRefresh := now.Add(-1 * time.Hour) 2957 s.state.Set("last-refresh", fakeLastRefresh) 2958 2959 tr := config.NewTransaction(s.state) 2960 tr.Set("core", "refresh.timer", fmt.Sprintf("00:00-%02d:%02d", now.Hour(), now.Minute())) 2961 tr.Commit() 2962 2963 // Ensure() also runs ensureRefreshes() 2964 s.state.Unlock() 2965 s.snapmgr.Ensure() 2966 s.state.Lock() 2967 2968 // nothing needs to be done and no refresh was run 2969 c.Check(s.state.Changes(), HasLen, 0) 2970 2971 var refreshLast time.Time 2972 s.state.Get("last-refresh", &refreshLast) 2973 c.Check(refreshLast.Equal(fakeLastRefresh), Equals, true) 2974 2975 // but a nextRefresh time got calculated 2976 nextRefresh = s.snapmgr.NextRefresh() 2977 c.Check(nextRefresh.IsZero(), Equals, false) 2978 2979 // run ensure again to test that nextRefresh again to ensure that 2980 // nextRefresh is not calculated again if nothing changes 2981 s.state.Unlock() 2982 s.snapmgr.Ensure() 2983 s.state.Lock() 2984 c.Check(s.snapmgr.NextRefresh(), Equals, nextRefresh) 2985 } 2986 2987 func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdate(c *C) { 2988 s.state.Lock() 2989 defer s.state.Unlock() 2990 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 2991 2992 makeTestRefreshConfig(s.state) 2993 2994 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 2995 Active: true, 2996 Sequence: []*snap.SideInfo{ 2997 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 2998 }, 2999 Current: snap.R(1), 3000 SnapType: "app", 3001 }) 3002 3003 // Ensure() also runs ensureRefreshes() and our test setup has an 3004 // update for the "some-snap" in our fake store 3005 s.state.Unlock() 3006 s.snapmgr.Ensure() 3007 s.state.Lock() 3008 3009 // verify we have an auto-refresh change scheduled now 3010 c.Assert(s.state.Changes(), HasLen, 1) 3011 chg := s.state.Changes()[0] 3012 c.Check(chg.Kind(), Equals, "auto-refresh") 3013 c.Check(chg.IsReady(), Equals, false) 3014 s.verifyRefreshLast(c) 3015 3016 checkIsAutoRefresh(c, chg.Tasks(), true) 3017 } 3018 3019 func (s *snapmgrTestSuite) TestEnsureRefreshesImmediateWithUpdate(c *C) { 3020 r := release.MockOnClassic(false) 3021 defer r() 3022 3023 s.state.Lock() 3024 defer s.state.Unlock() 3025 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 3026 3027 // lastRefresh is unset/zero => immediate refresh try 3028 3029 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 3030 Active: true, 3031 Sequence: []*snap.SideInfo{ 3032 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 3033 }, 3034 Current: snap.R(1), 3035 SnapType: "app", 3036 }) 3037 3038 // Ensure() also runs ensureRefreshes() and our test setup has an 3039 // update for the "some-snap" in our fake store 3040 s.state.Unlock() 3041 s.snapmgr.Ensure() 3042 s.state.Lock() 3043 3044 // verify we have an auto-refresh change scheduled now 3045 c.Assert(s.state.Changes(), HasLen, 1) 3046 chg := s.state.Changes()[0] 3047 c.Check(chg.Kind(), Equals, "auto-refresh") 3048 c.Check(chg.IsReady(), Equals, false) 3049 s.verifyRefreshLast(c) 3050 } 3051 3052 func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdateError(c *C) { 3053 s.state.Lock() 3054 defer s.state.Unlock() 3055 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 3056 3057 makeTestRefreshConfig(s.state) 3058 3059 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 3060 Active: true, 3061 Sequence: []*snap.SideInfo{ 3062 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 3063 }, 3064 Current: snap.R(1), 3065 SnapType: "app", 3066 }) 3067 3068 // Ensure() also runs ensureRefreshes() and our test setup has an 3069 // update for the "some-snap" in our fake store 3070 s.state.Unlock() 3071 s.snapmgr.Ensure() 3072 s.state.Lock() 3073 3074 c.Check(s.state.Changes(), HasLen, 1) 3075 chg := s.state.Changes()[0] 3076 terr := s.state.NewTask("error-trigger", "simulate an error") 3077 tasks := chg.Tasks() 3078 for _, t := range tasks[:len(tasks)-2] { 3079 terr.WaitFor(t) 3080 } 3081 chg.AddTask(terr) 3082 3083 // run the changes 3084 s.state.Unlock() 3085 s.settle(c) 3086 s.state.Lock() 3087 3088 s.verifyRefreshLast(c) 3089 } 3090 3091 func (s *snapmgrTestSuite) TestEnsureRefreshesInFlight(c *C) { 3092 s.state.Lock() 3093 defer s.state.Unlock() 3094 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 3095 3096 makeTestRefreshConfig(s.state) 3097 3098 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 3099 Active: true, 3100 Sequence: []*snap.SideInfo{ 3101 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 3102 }, 3103 Current: snap.R(1), 3104 SnapType: "app", 3105 }) 3106 3107 // simulate an in-flight change 3108 chg := s.state.NewChange("auto-refresh", "...") 3109 chg.SetStatus(state.DoStatus) 3110 c.Check(s.state.Changes(), HasLen, 1) 3111 3112 s.state.Unlock() 3113 s.snapmgr.Ensure() 3114 s.state.Lock() 3115 3116 // verify no additional change got generated 3117 c.Check(s.state.Changes(), HasLen, 1) 3118 } 3119 3120 func mockAutoRefreshAssertions(f func(st *state.State, userID int) error) func() { 3121 origAutoRefreshAssertions := snapstate.AutoRefreshAssertions 3122 snapstate.AutoRefreshAssertions = f 3123 return func() { 3124 snapstate.AutoRefreshAssertions = origAutoRefreshAssertions 3125 } 3126 } 3127 3128 func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdateStoreError(c *C) { 3129 s.state.Lock() 3130 defer s.state.Unlock() 3131 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 3132 3133 // avoid special at seed policy 3134 s.state.Set("last-refresh", time.Time{}) 3135 autoRefreshAssertionsCalled := 0 3136 restore := mockAutoRefreshAssertions(func(st *state.State, userID int) error { 3137 // simulate failure in snapstate.AutoRefresh() 3138 autoRefreshAssertionsCalled++ 3139 return fmt.Errorf("simulate store error") 3140 }) 3141 defer restore() 3142 3143 // check that no change got created and that autoRefreshAssertins 3144 // got called once 3145 s.state.Unlock() 3146 s.snapmgr.Ensure() 3147 s.state.Lock() 3148 c.Check(s.state.Changes(), HasLen, 0) 3149 c.Check(autoRefreshAssertionsCalled, Equals, 1) 3150 3151 // run Ensure() again and check that AutoRefresh() did not run 3152 // again because to test that lastRefreshAttempt backoff is working 3153 s.state.Unlock() 3154 s.snapmgr.Ensure() 3155 s.state.Lock() 3156 c.Check(s.state.Changes(), HasLen, 0) 3157 c.Check(autoRefreshAssertionsCalled, Equals, 1) 3158 } 3159 3160 func (s *snapmgrTestSuite) testEnsureRefreshesDisabledViaSnapdControl(c *C, confSet func(*config.Transaction)) { 3161 st := s.state 3162 st.Lock() 3163 defer st.Unlock() 3164 snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil } 3165 3166 makeTestRefreshConfig(st) 3167 3168 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 3169 Active: true, 3170 Sequence: []*snap.SideInfo{ 3171 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 3172 }, 3173 Current: snap.R(1), 3174 SnapType: "app", 3175 }) 3176 3177 // snapstate.AutoRefresh is called from AutoRefresh() 3178 autoRefreshAssertionsCalled := 0 3179 restore := mockAutoRefreshAssertions(func(st *state.State, userID int) error { 3180 autoRefreshAssertionsCalled++ 3181 return nil 3182 }) 3183 defer restore() 3184 3185 // pretend the device is refresh-control: managed 3186 oldCanManageRefreshes := snapstate.CanManageRefreshes 3187 snapstate.CanManageRefreshes = func(*state.State) bool { 3188 return true 3189 } 3190 defer func() { snapstate.CanManageRefreshes = oldCanManageRefreshes }() 3191 3192 tr := config.NewTransaction(st) 3193 confSet(tr) 3194 tr.Commit() 3195 3196 // Ensure() also runs ensureRefreshes() 3197 st.Unlock() 3198 s.snapmgr.Ensure() 3199 st.Lock() 3200 3201 // no refresh was called (i.e. no update to last-refresh) 3202 var lastRefresh time.Time 3203 st.Get("last-refresh", &lastRefresh) 3204 c.Check(lastRefresh.Year(), Equals, 2009) 3205 3206 // AutoRefresh was not called 3207 c.Check(autoRefreshAssertionsCalled, Equals, 0) 3208 3209 // The last refresh hints got updated 3210 var lastRefreshHints time.Time 3211 st.Get("last-refresh-hints", &lastRefreshHints) 3212 c.Check(lastRefreshHints.Year(), Equals, time.Now().Year()) 3213 } 3214 3215 func (s *snapmgrTestSuite) TestEnsureRefreshDisableLegacy(c *C) { 3216 f := func(tr *config.Transaction) { 3217 tr.Set("core", "refresh.timer", "") 3218 tr.Set("core", "refresh.schedule", "managed") 3219 } 3220 s.testEnsureRefreshesDisabledViaSnapdControl(c, f) 3221 } 3222 3223 func (s *snapmgrTestSuite) TestEnsureRefreshDisableNew(c *C) { 3224 f := func(tr *config.Transaction) { 3225 tr.Set("core", "refresh.timer", "managed") 3226 tr.Set("core", "refresh.schedule", "") 3227 } 3228 s.testEnsureRefreshesDisabledViaSnapdControl(c, f) 3229 } 3230 3231 func (s *snapmgrTestSuite) TestEnsureRefreshDisableNewTrumpsOld(c *C) { 3232 f := func(tr *config.Transaction) { 3233 tr.Set("core", "refresh.timer", "managed") 3234 tr.Set("core", "refresh.schedule", "00:00-12:00") 3235 } 3236 s.testEnsureRefreshesDisabledViaSnapdControl(c, f) 3237 } 3238 3239 func (s *snapmgrTestSuite) TestDefaultRefreshScheduleParsing(c *C) { 3240 l, err := timeutil.ParseSchedule(snapstate.DefaultRefreshSchedule) 3241 c.Assert(err, IsNil) 3242 c.Assert(l, HasLen, 1) 3243 } 3244 3245 func (s *snapmgrTestSuite) TestFinishRestartBasics(c *C) { 3246 r := release.MockOnClassic(true) 3247 defer r() 3248 3249 st := s.state 3250 st.Lock() 3251 defer st.Unlock() 3252 3253 task := st.NewTask("auto-connect", "...") 3254 3255 // not restarting 3256 state.MockRestarting(st, state.RestartUnset) 3257 si := &snap.SideInfo{RealName: "some-app"} 3258 snaptest.MockSnap(c, "name: some-app\nversion: 1", si) 3259 snapsup := &snapstate.SnapSetup{SideInfo: si} 3260 err := snapstate.FinishRestart(task, snapsup) 3261 c.Check(err, IsNil) 3262 3263 // restarting ... we always wait 3264 state.MockRestarting(st, state.RestartDaemon) 3265 err = snapstate.FinishRestart(task, snapsup) 3266 c.Check(err, FitsTypeOf, &state.Retry{}) 3267 } 3268 3269 func (s *snapmgrTestSuite) TestFinishRestartGeneratesSnapdWrappersOnCore(c *C) { 3270 r := release.MockOnClassic(false) 3271 defer r() 3272 3273 var generateWrappersCalled bool 3274 restore := snapstate.MockGenerateSnapdWrappers(func(snapInfo *snap.Info) error { 3275 c.Assert(snapInfo.SnapName(), Equals, "snapd") 3276 generateWrappersCalled = true 3277 return nil 3278 }) 3279 defer restore() 3280 3281 st := s.state 3282 st.Lock() 3283 defer st.Unlock() 3284 3285 for i, tc := range []struct { 3286 onClassic bool 3287 expectedWrappersCall bool 3288 snapName string 3289 snapYaml string 3290 }{ 3291 { 3292 onClassic: false, 3293 snapName: "snapd", 3294 snapYaml: `name: snapd 3295 type: snapd 3296 `, 3297 expectedWrappersCall: true, 3298 }, 3299 { 3300 onClassic: true, 3301 snapName: "snapd", 3302 snapYaml: `name: snapd 3303 type: snapd 3304 `, 3305 expectedWrappersCall: false, 3306 }, 3307 { 3308 onClassic: false, 3309 snapName: "some-snap", 3310 snapYaml: `name: some-snap`, 3311 expectedWrappersCall: false, 3312 }, 3313 } { 3314 generateWrappersCalled = false 3315 release.MockOnClassic(tc.onClassic) 3316 3317 task := st.NewTask("auto-connect", "...") 3318 si := &snap.SideInfo{Revision: snap.R("x2"), RealName: tc.snapName} 3319 snapInfo := snaptest.MockSnapCurrent(c, string(tc.snapYaml), si) 3320 snapsup := &snapstate.SnapSetup{SideInfo: si, Type: snapInfo.SnapType} 3321 3322 // restarting 3323 state.MockRestarting(st, state.RestartUnset) 3324 c.Assert(snapstate.FinishRestart(task, snapsup), IsNil) 3325 c.Check(generateWrappersCalled, Equals, tc.expectedWrappersCall, Commentf("#%d: %v", i, tc)) 3326 3327 c.Assert(os.RemoveAll(filepath.Join(snap.BaseDir(snapInfo.SnapName()), "current")), IsNil) 3328 } 3329 } 3330 3331 type snapmgrQuerySuite struct { 3332 st *state.State 3333 restore func() 3334 } 3335 3336 var _ = Suite(&snapmgrQuerySuite{}) 3337 3338 func (s *snapmgrQuerySuite) SetUpTest(c *C) { 3339 st := state.New(nil) 3340 st.Lock() 3341 defer st.Unlock() 3342 3343 restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 3344 s.restore = func() { 3345 restoreSanitize() 3346 } 3347 3348 s.st = st 3349 3350 dirs.SetRootDir(c.MkDir()) 3351 3352 // Write a snap.yaml with fake name 3353 sideInfo11 := &snap.SideInfo{RealName: "name1", Revision: snap.R(11), EditedSummary: "s11", SnapID: "123123123"} 3354 sideInfo12 := &snap.SideInfo{RealName: "name1", Revision: snap.R(12), EditedSummary: "s12", SnapID: "123123123"} 3355 instanceSideInfo13 := &snap.SideInfo{RealName: "name1", Revision: snap.R(13), EditedSummary: "s13 instance", SnapID: "123123123"} 3356 snaptest.MockSnap(c, ` 3357 name: name0 3358 version: 1.1 3359 description: | 3360 Lots of text`, sideInfo11) 3361 snaptest.MockSnap(c, ` 3362 name: name0 3363 version: 1.2 3364 description: | 3365 Lots of text`, sideInfo12) 3366 snaptest.MockSnapInstance(c, "name1_instance", ` 3367 name: name0 3368 version: 1.3 3369 description: | 3370 Lots of text`, instanceSideInfo13) 3371 snapstate.Set(st, "name1", &snapstate.SnapState{ 3372 Active: true, 3373 Sequence: []*snap.SideInfo{sideInfo11, sideInfo12}, 3374 Current: sideInfo12.Revision, 3375 SnapType: "app", 3376 }) 3377 snapstate.Set(st, "name1_instance", &snapstate.SnapState{ 3378 Active: true, 3379 Sequence: []*snap.SideInfo{instanceSideInfo13}, 3380 Current: instanceSideInfo13.Revision, 3381 SnapType: "app", 3382 InstanceKey: "instance", 3383 }) 3384 3385 // have also a snap being installed 3386 /* 3387 snapstate.Set(st, "installing", &snapstate.SnapState{ 3388 Candidate: &snap.SideInfo{RealName: "installing", Revision: snap.R(1)}, 3389 }) 3390 */ 3391 } 3392 3393 func (s *snapmgrQuerySuite) TearDownTest(c *C) { 3394 dirs.SetRootDir("") 3395 s.restore() 3396 } 3397 3398 func (s *snapmgrQuerySuite) TestInfo(c *C) { 3399 st := s.st 3400 st.Lock() 3401 defer st.Unlock() 3402 3403 info, err := snapstate.Info(st, "name1", snap.R(11)) 3404 c.Assert(err, IsNil) 3405 3406 c.Check(info.InstanceName(), Equals, "name1") 3407 c.Check(info.Revision, Equals, snap.R(11)) 3408 c.Check(info.Summary(), Equals, "s11") 3409 c.Check(info.Version, Equals, "1.1") 3410 c.Check(info.Description(), Equals, "Lots of text") 3411 } 3412 3413 func (s *snapmgrQuerySuite) TestSnapStateCurrentInfo(c *C) { 3414 st := s.st 3415 st.Lock() 3416 defer st.Unlock() 3417 3418 var snapst snapstate.SnapState 3419 err := snapstate.Get(st, "name1", &snapst) 3420 c.Assert(err, IsNil) 3421 3422 info, err := snapst.CurrentInfo() 3423 c.Assert(err, IsNil) 3424 3425 c.Check(info.InstanceName(), Equals, "name1") 3426 c.Check(info.Revision, Equals, snap.R(12)) 3427 c.Check(info.Summary(), Equals, "s12") 3428 c.Check(info.Version, Equals, "1.2") 3429 c.Check(info.Description(), Equals, "Lots of text") 3430 c.Check(info.Media, IsNil) 3431 c.Check(info.Website, Equals, "") 3432 } 3433 3434 func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoLoadsAuxiliaryStoreInfo(c *C) { 3435 storeInfo := &snapstate.AuxStoreInfo{ 3436 Media: snap.MediaInfos{{ 3437 Type: "icon", 3438 URL: "http://example.com/favicon.ico", 3439 }}, 3440 Website: "http://example.com/", 3441 } 3442 3443 c.Assert(snapstate.KeepAuxStoreInfo("123123123", storeInfo), IsNil) 3444 3445 st := s.st 3446 st.Lock() 3447 defer st.Unlock() 3448 3449 var snapst snapstate.SnapState 3450 err := snapstate.Get(st, "name1", &snapst) 3451 c.Assert(err, IsNil) 3452 3453 info, err := snapst.CurrentInfo() 3454 c.Assert(err, IsNil) 3455 3456 c.Check(info.InstanceName(), Equals, "name1") 3457 c.Check(info.Revision, Equals, snap.R(12)) 3458 c.Check(info.Summary(), Equals, "s12") 3459 c.Check(info.Version, Equals, "1.2") 3460 c.Check(info.Description(), Equals, "Lots of text") 3461 c.Check(info.Media, DeepEquals, storeInfo.Media) 3462 c.Check(info.Website, Equals, storeInfo.Website) 3463 } 3464 3465 func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoParallelInstall(c *C) { 3466 st := s.st 3467 st.Lock() 3468 defer st.Unlock() 3469 3470 var snapst snapstate.SnapState 3471 err := snapstate.Get(st, "name1_instance", &snapst) 3472 c.Assert(err, IsNil) 3473 3474 info, err := snapst.CurrentInfo() 3475 c.Assert(err, IsNil) 3476 3477 c.Check(info.InstanceName(), Equals, "name1_instance") 3478 c.Check(info.Revision, Equals, snap.R(13)) 3479 c.Check(info.Summary(), Equals, "s13 instance") 3480 c.Check(info.Version, Equals, "1.3") 3481 c.Check(info.Description(), Equals, "Lots of text") 3482 } 3483 3484 func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoErrNoCurrent(c *C) { 3485 snapst := new(snapstate.SnapState) 3486 _, err := snapst.CurrentInfo() 3487 c.Assert(err, Equals, snapstate.ErrNoCurrent) 3488 3489 } 3490 3491 func (s *snapmgrQuerySuite) TestCurrentInfo(c *C) { 3492 st := s.st 3493 st.Lock() 3494 defer st.Unlock() 3495 3496 info, err := snapstate.CurrentInfo(st, "name1") 3497 c.Assert(err, IsNil) 3498 3499 c.Check(info.InstanceName(), Equals, "name1") 3500 c.Check(info.Revision, Equals, snap.R(12)) 3501 } 3502 3503 func (s *snapmgrQuerySuite) TestCurrentInfoAbsent(c *C) { 3504 st := s.st 3505 st.Lock() 3506 defer st.Unlock() 3507 3508 _, err := snapstate.CurrentInfo(st, "absent") 3509 c.Assert(err, ErrorMatches, `snap "absent" is not installed`) 3510 } 3511 3512 func (s *snapmgrQuerySuite) TestActiveInfos(c *C) { 3513 st := s.st 3514 st.Lock() 3515 defer st.Unlock() 3516 3517 infos, err := snapstate.ActiveInfos(st) 3518 c.Assert(err, IsNil) 3519 3520 c.Check(infos, HasLen, 2) 3521 3522 instanceName := "name1_instance" 3523 if infos[0].InstanceName() != instanceName && infos[1].InstanceName() != instanceName { 3524 c.Fail() 3525 } 3526 // need stable ordering 3527 if infos[0].InstanceName() == instanceName { 3528 infos[1], infos[0] = infos[0], infos[1] 3529 } 3530 3531 c.Check(infos[0].InstanceName(), Equals, "name1") 3532 c.Check(infos[0].Revision, Equals, snap.R(12)) 3533 c.Check(infos[0].Summary(), Equals, "s12") 3534 c.Check(infos[0].Version, Equals, "1.2") 3535 c.Check(infos[0].Description(), Equals, "Lots of text") 3536 3537 c.Check(infos[1].InstanceName(), Equals, "name1_instance") 3538 c.Check(infos[1].Revision, Equals, snap.R(13)) 3539 c.Check(infos[1].Summary(), Equals, "s13 instance") 3540 c.Check(infos[1].Version, Equals, "1.3") 3541 c.Check(infos[1].Description(), Equals, "Lots of text") 3542 } 3543 3544 func (s *snapmgrQuerySuite) TestGadgetInfo(c *C) { 3545 st := s.st 3546 st.Lock() 3547 defer st.Unlock() 3548 3549 deviceCtxNoGadget := deviceWithoutGadgetContext() 3550 deviceCtx := deviceWithGadgetContext("gadget") 3551 3552 _, err := snapstate.GadgetInfo(st, deviceCtxNoGadget) 3553 c.Assert(err, Equals, state.ErrNoState) 3554 3555 _, err = snapstate.GadgetInfo(st, deviceCtx) 3556 c.Assert(err, Equals, state.ErrNoState) 3557 3558 sideInfo := &snap.SideInfo{ 3559 RealName: "gadget", 3560 Revision: snap.R(2), 3561 } 3562 snaptest.MockSnap(c, ` 3563 name: gadget 3564 type: gadget 3565 version: v1 3566 `, sideInfo) 3567 snapstate.Set(st, "gadget", &snapstate.SnapState{ 3568 SnapType: "gadget", 3569 Active: true, 3570 Sequence: []*snap.SideInfo{sideInfo}, 3571 Current: sideInfo.Revision, 3572 }) 3573 3574 info, err := snapstate.GadgetInfo(st, deviceCtx) 3575 c.Assert(err, IsNil) 3576 3577 c.Check(info.InstanceName(), Equals, "gadget") 3578 c.Check(info.Revision, Equals, snap.R(2)) 3579 c.Check(info.Version, Equals, "v1") 3580 c.Check(info.Type(), Equals, snap.TypeGadget) 3581 } 3582 3583 func (s *snapmgrQuerySuite) TestKernelInfo(c *C) { 3584 st := s.st 3585 st.Lock() 3586 defer st.Unlock() 3587 3588 deviceCtxNoKernel := &snapstatetest.TrivialDeviceContext{ 3589 DeviceModel: ClassicModel(), 3590 } 3591 deviceCtx := &snapstatetest.TrivialDeviceContext{ 3592 DeviceModel: MakeModel(map[string]interface{}{ 3593 "kernel": "pc-kernel", 3594 }), 3595 } 3596 3597 _, err := snapstate.KernelInfo(st, deviceCtxNoKernel) 3598 c.Assert(err, Equals, state.ErrNoState) 3599 3600 _, err = snapstate.KernelInfo(st, deviceCtx) 3601 c.Assert(err, Equals, state.ErrNoState) 3602 3603 sideInfo := &snap.SideInfo{ 3604 RealName: "pc-kernel", 3605 Revision: snap.R(3), 3606 } 3607 snaptest.MockSnap(c, ` 3608 name: pc-kernel 3609 type: kernel 3610 version: v2 3611 `, sideInfo) 3612 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 3613 SnapType: "kernel", 3614 Active: true, 3615 Sequence: []*snap.SideInfo{sideInfo}, 3616 Current: sideInfo.Revision, 3617 }) 3618 3619 info, err := snapstate.KernelInfo(st, deviceCtx) 3620 c.Assert(err, IsNil) 3621 3622 c.Check(info.InstanceName(), Equals, "pc-kernel") 3623 c.Check(info.Revision, Equals, snap.R(3)) 3624 c.Check(info.Version, Equals, "v2") 3625 c.Check(info.Type(), Equals, snap.TypeKernel) 3626 } 3627 3628 func (s *snapmgrQuerySuite) TestBootBaseInfo(c *C) { 3629 st := s.st 3630 st.Lock() 3631 defer st.Unlock() 3632 3633 deviceCtxNoBootBase := &snapstatetest.TrivialDeviceContext{ 3634 DeviceModel: ClassicModel(), 3635 } 3636 deviceCtx := &snapstatetest.TrivialDeviceContext{ 3637 DeviceModel: MakeModel20("gadget", map[string]interface{}{ 3638 "base": "core20", 3639 }), 3640 } 3641 3642 // add core18 which is *not* used for booting 3643 si := &snap.SideInfo{RealName: "core18", Revision: snap.R(1)} 3644 snaptest.MockSnap(c, ` 3645 name: core18 3646 type: base 3647 version: v18 3648 `, si) 3649 snapstate.Set(st, "core18", &snapstate.SnapState{ 3650 SnapType: "base", 3651 Active: true, 3652 Sequence: []*snap.SideInfo{si}, 3653 Current: si.Revision, 3654 }) 3655 3656 _, err := snapstate.BootBaseInfo(st, deviceCtxNoBootBase) 3657 c.Assert(err, Equals, state.ErrNoState) 3658 3659 // no boot-base in the state so ErrNoState 3660 _, err = snapstate.BootBaseInfo(st, deviceCtx) 3661 c.Assert(err, Equals, state.ErrNoState) 3662 3663 sideInfo := &snap.SideInfo{RealName: "core20", Revision: snap.R(4)} 3664 snaptest.MockSnap(c, ` 3665 name: core20 3666 type: base 3667 version: v20 3668 `, sideInfo) 3669 snapstate.Set(st, "core20", &snapstate.SnapState{ 3670 SnapType: "base", 3671 Active: true, 3672 Sequence: []*snap.SideInfo{sideInfo}, 3673 Current: sideInfo.Revision, 3674 }) 3675 3676 info, err := snapstate.BootBaseInfo(st, deviceCtx) 3677 c.Assert(err, IsNil) 3678 3679 c.Check(info.InstanceName(), Equals, "core20") 3680 c.Check(info.Revision, Equals, snap.R(4)) 3681 c.Check(info.Version, Equals, "v20") 3682 c.Check(info.Type(), Equals, snap.TypeBase) 3683 } 3684 3685 func (s *snapmgrQuerySuite) TestCoreInfoInternal(c *C) { 3686 st := s.st 3687 st.Lock() 3688 defer st.Unlock() 3689 3690 for testNr, t := range []struct { 3691 expectedSnap string 3692 snapNames []string 3693 errMatcher string 3694 }{ 3695 // nothing 3696 {"", []string{}, state.ErrNoState.Error()}, 3697 // single 3698 {"core", []string{"core"}, ""}, 3699 {"ubuntu-core", []string{"ubuntu-core"}, ""}, 3700 {"hard-core", []string{"hard-core"}, ""}, 3701 // unrolled loop to ensure we don't pass because 3702 // the order is randomly right 3703 {"core", []string{"core", "ubuntu-core"}, ""}, 3704 {"core", []string{"core", "ubuntu-core"}, ""}, 3705 {"core", []string{"core", "ubuntu-core"}, ""}, 3706 {"core", []string{"core", "ubuntu-core"}, ""}, 3707 {"core", []string{"core", "ubuntu-core"}, ""}, 3708 {"core", []string{"core", "ubuntu-core"}, ""}, 3709 {"core", []string{"core", "ubuntu-core"}, ""}, 3710 {"core", []string{"core", "ubuntu-core"}, ""}, 3711 // unknown combination 3712 {"", []string{"duo-core", "single-core"}, `unexpected cores.*`}, 3713 // multi-core is not supported 3714 {"", []string{"core", "ubuntu-core", "multi-core"}, `unexpected number of cores, got 3`}, 3715 } { 3716 // clear snapstate 3717 st.Set("snaps", map[string]*json.RawMessage{}) 3718 3719 for _, snapName := range t.snapNames { 3720 sideInfo := &snap.SideInfo{ 3721 RealName: snapName, 3722 Revision: snap.R(1), 3723 } 3724 snaptest.MockSnap(c, fmt.Sprintf("name: %q\ntype: os\nversion: %q\n", snapName, snapName), sideInfo) 3725 snapstate.Set(st, snapName, &snapstate.SnapState{ 3726 SnapType: string(snap.TypeOS), 3727 Active: true, 3728 Sequence: []*snap.SideInfo{sideInfo}, 3729 Current: sideInfo.Revision, 3730 }) 3731 } 3732 3733 info, err := snapstate.CoreInfoInternal(st) 3734 if t.errMatcher != "" { 3735 c.Assert(err, ErrorMatches, t.errMatcher) 3736 } else { 3737 c.Assert(info, NotNil) 3738 c.Check(info.InstanceName(), Equals, t.expectedSnap, Commentf("(%d) test %q %v", testNr, t.expectedSnap, t.snapNames)) 3739 c.Check(info.Type(), Equals, snap.TypeOS) 3740 } 3741 } 3742 } 3743 3744 func (s *snapmgrQuerySuite) TestHasSnapOfType(c *C) { 3745 st := s.st 3746 st.Lock() 3747 defer st.Unlock() 3748 3749 // an app snap is already setup 3750 ok, err := snapstate.HasSnapOfType(st, snap.TypeApp) 3751 c.Assert(err, IsNil) 3752 c.Check(ok, Equals, true) 3753 3754 for _, x := range []struct { 3755 snapName string 3756 snapType snap.Type 3757 }{ 3758 { 3759 snapName: "gadget", 3760 snapType: snap.TypeGadget, 3761 }, 3762 { 3763 snapName: "core", 3764 snapType: snap.TypeOS, 3765 }, 3766 { 3767 snapName: "kernel", 3768 snapType: snap.TypeKernel, 3769 }, 3770 { 3771 snapName: "base", 3772 snapType: snap.TypeBase, 3773 }, 3774 } { 3775 ok, err := snapstate.HasSnapOfType(st, x.snapType) 3776 c.Assert(err, IsNil) 3777 c.Check(ok, Equals, false, Commentf("%q", x.snapType)) 3778 3779 sideInfo := &snap.SideInfo{ 3780 RealName: x.snapName, 3781 Revision: snap.R(2), 3782 } 3783 snapstate.Set(st, x.snapName, &snapstate.SnapState{ 3784 SnapType: string(x.snapType), 3785 Active: true, 3786 Sequence: []*snap.SideInfo{sideInfo}, 3787 Current: sideInfo.Revision, 3788 }) 3789 3790 ok, err = snapstate.HasSnapOfType(st, x.snapType) 3791 c.Assert(err, IsNil) 3792 c.Check(ok, Equals, true) 3793 } 3794 } 3795 3796 func (s *snapmgrQuerySuite) TestPreviousSideInfo(c *C) { 3797 st := s.st 3798 st.Lock() 3799 defer st.Unlock() 3800 3801 var snapst snapstate.SnapState 3802 err := snapstate.Get(st, "name1", &snapst) 3803 c.Assert(err, IsNil) 3804 c.Assert(snapst.CurrentSideInfo(), NotNil) 3805 c.Assert(snapst.CurrentSideInfo().Revision, Equals, snap.R(12)) 3806 c.Assert(snapstate.PreviousSideInfo(&snapst), NotNil) 3807 c.Assert(snapstate.PreviousSideInfo(&snapst).Revision, Equals, snap.R(11)) 3808 } 3809 3810 func (s *snapmgrQuerySuite) TestPreviousSideInfoNoCurrent(c *C) { 3811 st := s.st 3812 st.Lock() 3813 defer st.Unlock() 3814 3815 snapst := &snapstate.SnapState{} 3816 c.Assert(snapstate.PreviousSideInfo(snapst), IsNil) 3817 } 3818 3819 func (s *snapmgrQuerySuite) TestAll(c *C) { 3820 st := s.st 3821 st.Lock() 3822 defer st.Unlock() 3823 3824 snapStates, err := snapstate.All(st) 3825 c.Assert(err, IsNil) 3826 c.Assert(snapStates, HasLen, 2) 3827 3828 n, err := snapstate.NumSnaps(st) 3829 c.Assert(err, IsNil) 3830 c.Check(n, Equals, 2) 3831 3832 snapst := snapStates["name1"] 3833 c.Assert(snapst, NotNil) 3834 3835 c.Check(snapst.Active, Equals, true) 3836 c.Check(snapst.CurrentSideInfo(), NotNil) 3837 3838 info12, err := snap.ReadInfo("name1", snapst.CurrentSideInfo()) 3839 c.Assert(err, IsNil) 3840 3841 c.Check(info12.InstanceName(), Equals, "name1") 3842 c.Check(info12.Revision, Equals, snap.R(12)) 3843 c.Check(info12.Summary(), Equals, "s12") 3844 c.Check(info12.Version, Equals, "1.2") 3845 c.Check(info12.Description(), Equals, "Lots of text") 3846 3847 info11, err := snap.ReadInfo("name1", snapst.Sequence[0]) 3848 c.Assert(err, IsNil) 3849 3850 c.Check(info11.InstanceName(), Equals, "name1") 3851 c.Check(info11.Revision, Equals, snap.R(11)) 3852 c.Check(info11.Version, Equals, "1.1") 3853 3854 instance := snapStates["name1_instance"] 3855 c.Assert(instance, NotNil) 3856 3857 c.Check(instance.Active, Equals, true) 3858 c.Check(instance.CurrentSideInfo(), NotNil) 3859 3860 info13, err := snap.ReadInfo("name1_instance", instance.CurrentSideInfo()) 3861 c.Assert(err, IsNil) 3862 3863 c.Check(info13.InstanceName(), Equals, "name1_instance") 3864 c.Check(info13.SnapName(), Equals, "name1") 3865 c.Check(info13.Revision, Equals, snap.R(13)) 3866 c.Check(info13.Summary(), Equals, "s13 instance") 3867 c.Check(info13.Version, Equals, "1.3") 3868 c.Check(info13.Description(), Equals, "Lots of text") 3869 3870 info13other, err := snap.ReadInfo("name1_instance", instance.Sequence[0]) 3871 c.Assert(err, IsNil) 3872 c.Check(info13, DeepEquals, info13other) 3873 } 3874 3875 func (s *snapmgrQuerySuite) TestAllEmptyAndEmptyNormalisation(c *C) { 3876 st := state.New(nil) 3877 st.Lock() 3878 defer st.Unlock() 3879 3880 snapStates, err := snapstate.All(st) 3881 c.Assert(err, IsNil) 3882 c.Check(snapStates, HasLen, 0) 3883 3884 n, err := snapstate.NumSnaps(st) 3885 c.Assert(err, IsNil) 3886 c.Check(n, Equals, 0) 3887 3888 snapstate.Set(st, "foo", nil) 3889 3890 snapStates, err = snapstate.All(st) 3891 c.Assert(err, IsNil) 3892 c.Check(snapStates, HasLen, 0) 3893 3894 n, err = snapstate.NumSnaps(st) 3895 c.Assert(err, IsNil) 3896 c.Check(n, Equals, 0) 3897 3898 snapstate.Set(st, "foo", &snapstate.SnapState{}) 3899 3900 snapStates, err = snapstate.All(st) 3901 c.Assert(err, IsNil) 3902 c.Check(snapStates, HasLen, 0) 3903 3904 n, err = snapstate.NumSnaps(st) 3905 c.Assert(err, IsNil) 3906 c.Check(n, Equals, 0) 3907 } 3908 3909 type snapStateSuite struct{} 3910 3911 var _ = Suite(&snapStateSuite{}) 3912 3913 func (s *snapStateSuite) TestSnapStateDevMode(c *C) { 3914 snapst := &snapstate.SnapState{} 3915 c.Check(snapst.DevMode, Equals, false) 3916 snapst.Flags.DevMode = true 3917 c.Check(snapst.DevMode, Equals, true) 3918 } 3919 3920 func (s *snapStateSuite) TestSnapStateType(c *C) { 3921 snapst := &snapstate.SnapState{} 3922 _, err := snapst.Type() 3923 c.Check(err, ErrorMatches, "snap type unset") 3924 3925 snapst.SetType(snap.TypeKernel) 3926 typ, err := snapst.Type() 3927 c.Assert(err, IsNil) 3928 c.Check(typ, Equals, snap.TypeKernel) 3929 } 3930 3931 func (s *snapStateSuite) TestCurrentSideInfoEmpty(c *C) { 3932 var snapst snapstate.SnapState 3933 c.Check(snapst.CurrentSideInfo(), IsNil) 3934 c.Check(snapst.Current.Unset(), Equals, true) 3935 } 3936 3937 func (s *snapStateSuite) TestCurrentSideInfoSimple(c *C) { 3938 si1 := &snap.SideInfo{Revision: snap.R(1)} 3939 snapst := snapstate.SnapState{ 3940 Sequence: []*snap.SideInfo{si1}, 3941 Current: snap.R(1), 3942 } 3943 c.Check(snapst.CurrentSideInfo(), DeepEquals, si1) 3944 } 3945 3946 func (s *snapStateSuite) TestCurrentSideInfoInOrder(c *C) { 3947 si1 := &snap.SideInfo{Revision: snap.R(1)} 3948 si2 := &snap.SideInfo{Revision: snap.R(2)} 3949 snapst := snapstate.SnapState{ 3950 Sequence: []*snap.SideInfo{si1, si2}, 3951 Current: snap.R(2), 3952 } 3953 c.Check(snapst.CurrentSideInfo(), DeepEquals, si2) 3954 } 3955 3956 func (s *snapStateSuite) TestCurrentSideInfoOutOfOrder(c *C) { 3957 si1 := &snap.SideInfo{Revision: snap.R(1)} 3958 si2 := &snap.SideInfo{Revision: snap.R(2)} 3959 snapst := snapstate.SnapState{ 3960 Sequence: []*snap.SideInfo{si1, si2}, 3961 Current: snap.R(1), 3962 } 3963 c.Check(snapst.CurrentSideInfo(), DeepEquals, si1) 3964 } 3965 3966 func (s *snapStateSuite) TestCurrentSideInfoInconsistent(c *C) { 3967 snapst := snapstate.SnapState{ 3968 Sequence: []*snap.SideInfo{ 3969 {Revision: snap.R(1)}, 3970 }, 3971 } 3972 c.Check(func() { snapst.CurrentSideInfo() }, PanicMatches, `snapst.Current and snapst.Sequence out of sync:.*`) 3973 } 3974 3975 func (s *snapStateSuite) TestCurrentSideInfoInconsistentWithCurrent(c *C) { 3976 snapst := snapstate.SnapState{Current: snap.R(17)} 3977 c.Check(func() { snapst.CurrentSideInfo() }, PanicMatches, `cannot find snapst.Current in the snapst.Sequence`) 3978 } 3979 3980 func (snapStateSuite) TestDefaultContentPlugProviders(c *C) { 3981 info := &snap.Info{ 3982 Plugs: map[string]*snap.PlugInfo{}, 3983 } 3984 3985 info.Plugs["foo"] = &snap.PlugInfo{ 3986 Snap: info, 3987 Name: "sound-themes", 3988 Interface: "content", 3989 Attrs: map[string]interface{}{"default-provider": "common-themes", "content": "foo"}, 3990 } 3991 info.Plugs["bar"] = &snap.PlugInfo{ 3992 Snap: info, 3993 Name: "visual-themes", 3994 Interface: "content", 3995 Attrs: map[string]interface{}{"default-provider": "common-themes", "content": "bar"}, 3996 } 3997 info.Plugs["baz"] = &snap.PlugInfo{ 3998 Snap: info, 3999 Name: "not-themes", 4000 Interface: "content", 4001 Attrs: map[string]interface{}{"default-provider": "some-snap", "content": "baz"}, 4002 } 4003 info.Plugs["qux"] = &snap.PlugInfo{Snap: info, Interface: "not-content"} 4004 4005 st := state.New(nil) 4006 st.Lock() 4007 defer st.Unlock() 4008 4009 repo := interfaces.NewRepository() 4010 ifacerepo.Replace(st, repo) 4011 4012 providers := snapstate.DefaultContentPlugProviders(st, info) 4013 sort.Strings(providers) 4014 c.Check(providers, DeepEquals, []string{"common-themes", "some-snap"}) 4015 } 4016 4017 func (s *snapmgrTestSuite) testRevertSequence(c *C, opts *opSeqOpts) *state.TaskSet { 4018 opts.revert = true 4019 opts.after = opts.before 4020 snapst, ts := s.testOpSequence(c, opts) 4021 // successful revert leaves current == via 4022 c.Check(snapst.Current.N, Equals, opts.via) 4023 4024 c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0) 4025 4026 return ts 4027 } 4028 4029 func (s *snapmgrTestSuite) testRevertFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet { 4030 opts.revert = true 4031 opts.after = opts.before 4032 s.fakeBackend.linkSnapFailTrigger = fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via) 4033 snapst, ts := s.testOpSequence(c, opts) 4034 // a failed revert will always end with current unchanged 4035 c.Check(snapst.Current.N, Equals, opts.current) 4036 4037 c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0) 4038 c.Check(s.fakeBackend.ops.Count("undo-copy-snap-data"), Equals, 0) 4039 4040 return ts 4041 } 4042 4043 func (s *snapmgrTestSuite) testTotalRevertFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet { 4044 opts.revert = true 4045 opts.fail = true 4046 opts.after = opts.before 4047 snapst, ts := s.testOpSequence(c, opts) 4048 // a failed revert will always end with current unchanged 4049 c.Check(snapst.Current.N, Equals, opts.current) 4050 4051 c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0) 4052 c.Check(s.fakeBackend.ops.Count("undo-copy-snap-data"), Equals, 0) 4053 4054 return ts 4055 } 4056 4057 // *** sequence tests *** 4058 4059 // 1. a boring update 4060 // 1a. ... that works 4061 func (s *snapmgrTestSuite) TestSeqNormal(c *C) { 4062 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4, after: []int{2, 3, 4}}) 4063 } 4064 4065 // 1b. that fails during link 4066 func (s *snapmgrTestSuite) TestSeqNormalFailure(c *C) { 4067 s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4}) 4068 } 4069 4070 // 1c. that fails after link 4071 func (s *snapmgrTestSuite) TestSeqTotalNormalFailure(c *C) { 4072 // total updates are failures after sequence trimming => we lose a rev 4073 s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4, after: []int{2, 3}}) 4074 } 4075 4076 // 2. a boring revert 4077 // 2a. that works 4078 func (s *snapmgrTestSuite) TestSeqRevert(c *C) { 4079 s.testRevertSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2}) 4080 } 4081 4082 // 2b. that fails during link 4083 func (s *snapmgrTestSuite) TestSeqRevertFailure(c *C) { 4084 s.testRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2}) 4085 } 4086 4087 // 2c. that fails after link 4088 func (s *snapmgrTestSuite) TestSeqTotalRevertFailure(c *C) { 4089 s.testTotalRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2}) 4090 } 4091 4092 // 3. a post-revert update 4093 // 3a. that works 4094 func (s *snapmgrTestSuite) TestSeqPostRevert(c *C) { 4095 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4, after: []int{1, 2, 4}}) 4096 } 4097 4098 // 3b. that fails during link 4099 func (s *snapmgrTestSuite) TestSeqPostRevertFailure(c *C) { 4100 s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4}) 4101 } 4102 4103 // 3c. that fails after link 4104 func (s *snapmgrTestSuite) TestSeqTotalPostRevertFailure(c *C) { 4105 // lose a rev here as well 4106 s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4, after: []int{1, 2}}) 4107 } 4108 4109 // 3d. manually requesting the one reverted away from 4110 func (s *snapmgrTestSuite) TestSeqRefreshPostRevertSameRevno(c *C) { 4111 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 3, after: []int{1, 2, 3}}) 4112 } 4113 4114 // 4. a post-revert revert 4115 // 4a. that works 4116 func (s *snapmgrTestSuite) TestSeqRevertPostRevert(c *C) { 4117 s.testRevertSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1}) 4118 } 4119 4120 // 4b. that fails during link 4121 func (s *snapmgrTestSuite) TestSeqRevertPostRevertFailure(c *C) { 4122 s.testRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1}) 4123 } 4124 4125 // 4c. that fails after link 4126 func (s *snapmgrTestSuite) TestSeqTotalRevertPostRevertFailure(c *C) { 4127 s.testTotalRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1}) 4128 } 4129 4130 // 5. an update that missed a rev 4131 // 5a. that works 4132 func (s *snapmgrTestSuite) TestSeqMissedOne(c *C) { 4133 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4, after: []int{1, 2, 4}}) 4134 } 4135 4136 // 5b. that fails during link 4137 func (s *snapmgrTestSuite) TestSeqMissedOneFailure(c *C) { 4138 s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4}) 4139 } 4140 4141 // 5c. that fails after link 4142 func (s *snapmgrTestSuite) TestSeqTotalMissedOneFailure(c *C) { 4143 // we don't lose a rev here because len(Seq) < 3 going in 4144 s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4, after: []int{1, 2}}) 4145 } 4146 4147 // 6. an update that updates to a revision we already have ("ABA update") 4148 // 6a. that works 4149 func (s *snapmgrTestSuite) TestSeqABA(c *C) { 4150 s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2, after: []int{1, 3, 2}}) 4151 c.Check(s.fakeBackend.ops[len(s.fakeBackend.ops)-1], DeepEquals, fakeOp{ 4152 op: "cleanup-trash", 4153 name: "some-snap", 4154 revno: snap.R(2), 4155 }) 4156 } 4157 4158 // 6b. that fails during link 4159 func (s *snapmgrTestSuite) TestSeqABAFailure(c *C) { 4160 s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2}) 4161 c.Check(s.fakeBackend.ops.First("cleanup-trash"), IsNil) 4162 } 4163 4164 // 6c that fails after link 4165 func (s *snapmgrTestSuite) TestSeqTotalABAFailure(c *C) { 4166 // we don't lose a rev here because ABA 4167 s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2, after: []int{1, 2, 3}}) 4168 // XXX: TODO: NOTE!! WARNING!! etc 4169 // 4170 // if this happens in real life, things will be weird. revno 2 will 4171 // have data that has been copied from 3, instead of old 2's data, 4172 // because the failure occurred *after* nuking the trash. This can 4173 // happen when things are chained. Because of this, if it were to 4174 // *actually* happen the correct end sequence would be [1, 3] and not 4175 // [1, 2, 3]. IRL this scenario can happen if an update that works is 4176 // chained to an update that fails. Detecting this case is rather hard, 4177 // and the end result is not nice, and we want to move cleanup to a 4178 // separate handler & status that will cope with this better (so trash 4179 // gets nuked after all tasks succeeded). 4180 } 4181 4182 func (s *snapmgrTestSuite) TestSeqRetainConf(c *C) { 4183 revseq := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 4184 4185 for i := 2; i <= 10; i++ { 4186 // wot, me, hacky? 4187 s.TearDownTest(c) 4188 s.SetUpTest(c) 4189 s.state.Lock() 4190 tr := config.NewTransaction(s.state) 4191 tr.Set("core", "refresh.retain", i) 4192 tr.Commit() 4193 s.state.Unlock() 4194 4195 s.testUpdateSequence(c, &opSeqOpts{before: revseq[:9], current: 9, via: 10, after: revseq[10-i:]}) 4196 } 4197 } 4198 4199 func (s *snapmgrTestSuite) TestSnapStateNoLocalRevision(c *C) { 4200 si7 := snap.SideInfo{ 4201 RealName: "some-snap", 4202 Revision: snap.R(-7), 4203 } 4204 si11 := snap.SideInfo{ 4205 RealName: "some-snap", 4206 Revision: snap.R(-11), 4207 } 4208 snapst := &snapstate.SnapState{ 4209 Sequence: []*snap.SideInfo{&si7, &si11}, 4210 Current: si7.Revision, 4211 } 4212 c.Assert(snapst.LocalRevision(), Equals, snap.R(-11)) 4213 } 4214 4215 func (s *snapmgrTestSuite) TestSnapStateLocalRevision(c *C) { 4216 si7 := snap.SideInfo{ 4217 RealName: "some-snap", 4218 Revision: snap.R(7), 4219 } 4220 snapst := &snapstate.SnapState{ 4221 Sequence: []*snap.SideInfo{&si7}, 4222 Current: si7.Revision, 4223 } 4224 c.Assert(snapst.LocalRevision().Unset(), Equals, true) 4225 } 4226 4227 func tasksWithKind(ts *state.TaskSet, kind string) []*state.Task { 4228 var tasks []*state.Task 4229 for _, task := range ts.Tasks() { 4230 if task.Kind() == kind { 4231 tasks = append(tasks, task) 4232 } 4233 } 4234 return tasks 4235 } 4236 4237 var gadgetYaml = ` 4238 defaults: 4239 somesnapidididididididididididid: 4240 key: value 4241 4242 volumes: 4243 volume-id: 4244 bootloader: grub 4245 ` 4246 4247 func (s *snapmgrTestSuite) prepareGadget(c *C, extraGadgetYaml ...string) { 4248 gadgetSideInfo := &snap.SideInfo{RealName: "the-gadget", SnapID: "the-gadget-id", Revision: snap.R(1)} 4249 gadgetInfo := snaptest.MockSnap(c, ` 4250 name: the-gadget 4251 type: gadget 4252 version: 1.0 4253 `, gadgetSideInfo) 4254 4255 gadgetYamlWhole := strings.Join(append([]string{gadgetYaml}, extraGadgetYaml...), "") 4256 err := ioutil.WriteFile(filepath.Join(gadgetInfo.MountDir(), "meta/gadget.yaml"), []byte(gadgetYamlWhole), 0600) 4257 c.Assert(err, IsNil) 4258 4259 snapstate.Set(s.state, "the-gadget", &snapstate.SnapState{ 4260 Active: true, 4261 Sequence: []*snap.SideInfo{&gadgetInfo.SideInfo}, 4262 Current: snap.R(1), 4263 SnapType: "gadget", 4264 }) 4265 } 4266 4267 func deviceWithGadgetContext(gadgetName string) snapstate.DeviceContext { 4268 return &snapstatetest.TrivialDeviceContext{ 4269 DeviceModel: MakeModel(map[string]interface{}{ 4270 "gadget": gadgetName, 4271 }), 4272 } 4273 } 4274 4275 func deviceWithGadgetContext20(gadgetName string) snapstate.DeviceContext { 4276 return &snapstatetest.TrivialDeviceContext{ 4277 DeviceModel: MakeModel20(gadgetName, nil), 4278 } 4279 } 4280 4281 func deviceWithoutGadgetContext() snapstate.DeviceContext { 4282 return &snapstatetest.TrivialDeviceContext{ 4283 DeviceModel: ClassicModel(), 4284 } 4285 } 4286 4287 func verifyTransitionConnectionsTasks(c *C, ts *state.TaskSet) { 4288 c.Check(taskKinds(ts.Tasks()), DeepEquals, []string{ 4289 "transition-ubuntu-core", 4290 }) 4291 4292 transIf := ts.Tasks()[0] 4293 var oldName, newName string 4294 err := transIf.Get("old-name", &oldName) 4295 c.Assert(err, IsNil) 4296 c.Check(oldName, Equals, "ubuntu-core") 4297 4298 err = transIf.Get("new-name", &newName) 4299 c.Assert(err, IsNil) 4300 c.Check(newName, Equals, "core") 4301 } 4302 4303 func (s *snapmgrTestSuite) TestTransitionCoreTasks(c *C) { 4304 s.state.Lock() 4305 defer s.state.Unlock() 4306 4307 snapstate.Set(s.state, "core", nil) 4308 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 4309 Active: true, 4310 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 4311 Current: snap.R(1), 4312 SnapType: "os", 4313 }) 4314 4315 tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core") 4316 c.Assert(err, IsNil) 4317 4318 c.Assert(tsl, HasLen, 3) 4319 // 1. install core 4320 verifyInstallTasks(c, runCoreConfigure|maybeCore, 0, tsl[0], s.state) 4321 // 2 transition-connections 4322 verifyTransitionConnectionsTasks(c, tsl[1]) 4323 // 3 remove-ubuntu-core 4324 verifyCoreRemoveTasks(c, tsl[2]) 4325 } 4326 4327 func (s *snapmgrTestSuite) TestTransitionCoreTasksWithUbuntuCoreAndCore(c *C) { 4328 s.state.Lock() 4329 defer s.state.Unlock() 4330 4331 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 4332 Active: true, 4333 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 4334 Current: snap.R(1), 4335 SnapType: "os", 4336 }) 4337 snapstate.Set(s.state, "core", &snapstate.SnapState{ 4338 Active: true, 4339 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 4340 Current: snap.R(1), 4341 SnapType: "os", 4342 }) 4343 4344 tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core") 4345 c.Assert(err, IsNil) 4346 4347 c.Assert(tsl, HasLen, 2) 4348 // 1. transition connections 4349 verifyTransitionConnectionsTasks(c, tsl[0]) 4350 // 2. remove ubuntu-core 4351 verifyCoreRemoveTasks(c, tsl[1]) 4352 } 4353 4354 func (s *snapmgrTestSuite) TestTransitionCoreRunThrough(c *C) { 4355 s.state.Lock() 4356 defer s.state.Unlock() 4357 4358 snapstate.Set(s.state, "core", nil) 4359 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 4360 Active: true, 4361 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 4362 Current: snap.R(1), 4363 SnapType: "os", 4364 TrackingChannel: "latest/beta", 4365 }) 4366 4367 chg := s.state.NewChange("transition-ubuntu-core", "...") 4368 tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core") 4369 c.Assert(err, IsNil) 4370 for _, ts := range tsl { 4371 chg.AddAll(ts) 4372 } 4373 4374 s.state.Unlock() 4375 defer s.se.Stop() 4376 s.settle(c) 4377 s.state.Lock() 4378 4379 // ensure all our tasks ran 4380 c.Assert(chg.Err(), IsNil) 4381 c.Assert(chg.IsReady(), Equals, true) 4382 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 4383 name: "core", 4384 // the transition has no user associcated with it 4385 macaroon: "", 4386 target: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 4387 }}) 4388 expected := fakeOps{ 4389 { 4390 op: "storesvc-snap-action", 4391 curSnaps: []store.CurrentSnap{ 4392 { 4393 InstanceName: "ubuntu-core", 4394 SnapID: "ubuntu-core-snap-id", 4395 Revision: snap.R(1), 4396 TrackingChannel: "latest/beta", 4397 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1), 4398 Epoch: snap.E("1*"), 4399 }, 4400 }, 4401 }, 4402 { 4403 op: "storesvc-snap-action:action", 4404 action: store.SnapAction{ 4405 Action: "install", 4406 InstanceName: "core", 4407 Channel: "latest/beta", 4408 }, 4409 revno: snap.R(11), 4410 }, 4411 { 4412 op: "storesvc-download", 4413 name: "core", 4414 }, 4415 { 4416 op: "validate-snap:Doing", 4417 name: "core", 4418 revno: snap.R(11), 4419 }, 4420 { 4421 op: "current", 4422 old: "<no-current>", 4423 }, 4424 { 4425 op: "open-snap-file", 4426 path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 4427 sinfo: snap.SideInfo{ 4428 RealName: "core", 4429 SnapID: "core-id", 4430 Channel: "latest/beta", 4431 Revision: snap.R(11), 4432 }, 4433 }, 4434 { 4435 op: "setup-snap", 4436 name: "core", 4437 path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 4438 revno: snap.R(11), 4439 }, 4440 { 4441 op: "copy-data", 4442 path: filepath.Join(dirs.SnapMountDir, "core/11"), 4443 old: "<no-old>", 4444 }, 4445 { 4446 op: "setup-profiles:Doing", 4447 name: "core", 4448 revno: snap.R(11), 4449 }, 4450 { 4451 op: "candidate", 4452 sinfo: snap.SideInfo{ 4453 RealName: "core", 4454 SnapID: "core-id", 4455 Channel: "latest/beta", 4456 Revision: snap.R(11), 4457 }, 4458 }, 4459 { 4460 op: "link-snap", 4461 path: filepath.Join(dirs.SnapMountDir, "core/11"), 4462 }, 4463 { 4464 op: "auto-connect:Doing", 4465 name: "core", 4466 revno: snap.R(11), 4467 }, 4468 { 4469 op: "update-aliases", 4470 }, 4471 { 4472 op: "transition-ubuntu-core:Doing", 4473 name: "ubuntu-core", 4474 }, 4475 { 4476 op: "auto-disconnect:Doing", 4477 name: "ubuntu-core", 4478 revno: snap.R(1), 4479 }, 4480 { 4481 op: "remove-snap-aliases", 4482 name: "ubuntu-core", 4483 }, 4484 { 4485 op: "unlink-snap", 4486 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 4487 }, 4488 { 4489 op: "remove-profiles:Doing", 4490 name: "ubuntu-core", 4491 revno: snap.R(1), 4492 }, 4493 { 4494 op: "remove-snap-data", 4495 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 4496 }, 4497 { 4498 op: "remove-snap-common-data", 4499 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 4500 }, 4501 { 4502 op: "remove-snap-data-dir", 4503 name: "ubuntu-core", 4504 path: filepath.Join(dirs.SnapDataDir, "ubuntu-core"), 4505 }, 4506 { 4507 op: "remove-snap-files", 4508 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 4509 stype: "os", 4510 }, 4511 { 4512 op: "discard-namespace", 4513 name: "ubuntu-core", 4514 }, 4515 { 4516 op: "remove-inhibit-lock", 4517 name: "ubuntu-core", 4518 }, 4519 { 4520 op: "remove-snap-dir", 4521 name: "ubuntu-core", 4522 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core"), 4523 }, 4524 { 4525 op: "cleanup-trash", 4526 name: "core", 4527 revno: snap.R(11), 4528 }, 4529 } 4530 // start with an easier-to-read error if this fails: 4531 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 4532 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 4533 } 4534 4535 func (s *snapmgrTestSuite) TestTransitionCoreRunThroughWithCore(c *C) { 4536 s.state.Lock() 4537 defer s.state.Unlock() 4538 4539 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 4540 Active: true, 4541 Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}}, 4542 Current: snap.R(1), 4543 SnapType: "os", 4544 TrackingChannel: "latest/stable", 4545 }) 4546 snapstate.Set(s.state, "core", &snapstate.SnapState{ 4547 Active: true, 4548 Sequence: []*snap.SideInfo{{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(1)}}, 4549 Current: snap.R(1), 4550 SnapType: "os", 4551 TrackingChannel: "latest/stable", 4552 }) 4553 4554 chg := s.state.NewChange("transition-ubuntu-core", "...") 4555 tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core") 4556 c.Assert(err, IsNil) 4557 for _, ts := range tsl { 4558 chg.AddAll(ts) 4559 } 4560 4561 s.state.Unlock() 4562 defer s.se.Stop() 4563 s.settle(c) 4564 s.state.Lock() 4565 4566 // ensure all our tasks ran 4567 c.Assert(chg.Err(), IsNil) 4568 c.Assert(chg.IsReady(), Equals, true) 4569 c.Check(s.fakeStore.downloads, HasLen, 0) 4570 expected := fakeOps{ 4571 { 4572 op: "transition-ubuntu-core:Doing", 4573 name: "ubuntu-core", 4574 }, 4575 { 4576 op: "auto-disconnect:Doing", 4577 name: "ubuntu-core", 4578 revno: snap.R(1), 4579 }, 4580 { 4581 op: "remove-snap-aliases", 4582 name: "ubuntu-core", 4583 }, 4584 { 4585 op: "unlink-snap", 4586 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 4587 }, 4588 { 4589 op: "remove-profiles:Doing", 4590 name: "ubuntu-core", 4591 revno: snap.R(1), 4592 }, 4593 { 4594 op: "remove-snap-data", 4595 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 4596 }, 4597 { 4598 op: "remove-snap-common-data", 4599 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 4600 }, 4601 { 4602 op: "remove-snap-data-dir", 4603 name: "ubuntu-core", 4604 path: filepath.Join(dirs.SnapDataDir, "ubuntu-core"), 4605 }, 4606 { 4607 op: "remove-snap-files", 4608 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"), 4609 stype: "os", 4610 }, 4611 { 4612 op: "discard-namespace", 4613 name: "ubuntu-core", 4614 }, 4615 { 4616 op: "remove-inhibit-lock", 4617 name: "ubuntu-core", 4618 }, 4619 { 4620 op: "remove-snap-dir", 4621 name: "ubuntu-core", 4622 path: filepath.Join(dirs.SnapMountDir, "ubuntu-core"), 4623 }, 4624 } 4625 // start with an easier-to-read error if this fails: 4626 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 4627 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 4628 } 4629 4630 func (s *snapmgrTestSuite) TestTransitionCoreStartsAutomatically(c *C) { 4631 s.state.Lock() 4632 defer s.state.Unlock() 4633 4634 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 4635 Active: true, 4636 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}}, 4637 Current: snap.R(1), 4638 SnapType: "os", 4639 }) 4640 4641 s.state.Unlock() 4642 defer s.se.Stop() 4643 s.settle(c) 4644 s.state.Lock() 4645 4646 c.Check(s.state.Changes(), HasLen, 1) 4647 c.Check(s.state.Changes()[0].Kind(), Equals, "transition-ubuntu-core") 4648 } 4649 4650 func (s *snapmgrTestSuite) TestTransitionCoreTooEarly(c *C) { 4651 s.state.Lock() 4652 defer s.state.Unlock() 4653 4654 r := snapstatetest.MockDeviceModel(nil) 4655 defer r() 4656 4657 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 4658 Active: true, 4659 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}}, 4660 Current: snap.R(1), 4661 SnapType: "os", 4662 }) 4663 4664 s.state.Unlock() 4665 defer s.se.Stop() 4666 s.settle(c) 4667 s.state.Lock() 4668 4669 c.Check(s.state.Changes(), HasLen, 0) 4670 // not counted as a try 4671 var t time.Time 4672 err := s.state.Get("ubuntu-core-transition-last-retry-time", &t) 4673 c.Assert(err, Equals, state.ErrNoState) 4674 } 4675 4676 func (s *snapmgrTestSuite) TestTransitionCoreTimeLimitWorks(c *C) { 4677 s.state.Lock() 4678 defer s.state.Unlock() 4679 4680 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 4681 Active: true, 4682 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}}, 4683 Current: snap.R(1), 4684 SnapType: "os", 4685 }) 4686 4687 // tried 3h ago, no retry 4688 s.state.Set("ubuntu-core-transition-last-retry-time", time.Now().Add(-3*time.Hour)) 4689 4690 s.state.Unlock() 4691 defer s.se.Stop() 4692 s.settle(c) 4693 s.state.Lock() 4694 4695 c.Check(s.state.Changes(), HasLen, 0) 4696 4697 // tried 7h ago, retry 4698 s.state.Set("ubuntu-core-transition-last-retry-time", time.Now().Add(-7*time.Hour)) 4699 4700 s.state.Unlock() 4701 defer s.se.Stop() 4702 s.settle(c) 4703 s.state.Lock() 4704 c.Check(s.state.Changes(), HasLen, 1) 4705 4706 var t time.Time 4707 s.state.Get("ubuntu-core-transition-last-retry-time", &t) 4708 c.Assert(time.Since(t) < 2*time.Minute, Equals, true) 4709 } 4710 4711 func (s *snapmgrTestSuite) TestTransitionCoreNoOtherChanges(c *C) { 4712 s.state.Lock() 4713 defer s.state.Unlock() 4714 4715 snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{ 4716 Active: true, 4717 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}}, 4718 Current: snap.R(1), 4719 SnapType: "os", 4720 }) 4721 chg := s.state.NewChange("unrelated-change", "unfinished change blocks core transition") 4722 chg.SetStatus(state.DoStatus) 4723 4724 s.state.Unlock() 4725 defer s.se.Stop() 4726 s.settle(c) 4727 s.state.Lock() 4728 4729 c.Check(s.state.Changes(), HasLen, 1) 4730 c.Check(s.state.Changes()[0].Kind(), Equals, "unrelated-change") 4731 } 4732 4733 func (s *snapmgrTestSuite) TestTransitionCoreBlocksOtherChanges(c *C) { 4734 s.state.Lock() 4735 defer s.state.Unlock() 4736 4737 // if we have a ubuntu-core -> core transition 4738 chg := s.state.NewChange("transition-ubuntu-core", "...") 4739 chg.SetStatus(state.DoStatus) 4740 4741 // other tasks block until the transition is done 4742 opts := &snapstate.RevisionOptions{Channel: "stable"} 4743 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 4744 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 4745 c.Check(err, ErrorMatches, "ubuntu-core to core transition in progress, no other changes allowed until this is done") 4746 4747 // and when the transition is done, other tasks run 4748 chg.SetStatus(state.DoneStatus) 4749 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 4750 c.Check(err, IsNil) 4751 c.Check(ts, NotNil) 4752 } 4753 4754 func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesNotRunWithoutSnaps(c *C) { 4755 s.state.Lock() 4756 defer s.state.Unlock() 4757 4758 tr := config.NewTransaction(s.state) 4759 tr.Set("core", "experimental.snapd-snap", true) 4760 tr.Commit() 4761 4762 // no snaps installed on this system (e.g. fresh classic) 4763 snapstate.Set(s.state, "core", nil) 4764 4765 s.state.Unlock() 4766 defer s.se.Stop() 4767 s.settle(c) 4768 s.state.Lock() 4769 4770 c.Check(s.state.Changes(), HasLen, 0) 4771 } 4772 4773 func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesRunWithAnySnap(c *C) { 4774 s.state.Lock() 4775 defer s.state.Unlock() 4776 4777 tr := config.NewTransaction(s.state) 4778 tr.Set("core", "experimental.snapd-snap", true) 4779 tr.Commit() 4780 4781 // some snap installed on this system but no core 4782 snapstate.Set(s.state, "core", nil) 4783 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 4784 Active: true, 4785 Sequence: []*snap.SideInfo{{RealName: "foo", SnapID: "foo-id", Revision: snap.R(1), Channel: "beta"}}, 4786 Current: snap.R(1), 4787 }) 4788 4789 s.state.Unlock() 4790 defer s.se.Stop() 4791 s.settle(c) 4792 s.state.Lock() 4793 4794 c.Check(s.state.Changes(), HasLen, 1) 4795 } 4796 4797 func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesNotRunWhenNotEnabled(c *C) { 4798 s.state.Lock() 4799 defer s.state.Unlock() 4800 4801 snapstate.Set(s.state, "core", &snapstate.SnapState{ 4802 Active: true, 4803 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "beta"}}, 4804 Current: snap.R(1), 4805 SnapType: "os", 4806 }) 4807 4808 s.state.Unlock() 4809 defer s.se.Stop() 4810 s.settle(c) 4811 s.state.Lock() 4812 4813 c.Check(s.state.Changes(), HasLen, 0) 4814 } 4815 4816 func (s *snapmgrTestSuite) TestTransitionSnapdSnapStartsAutomaticallyWhenEnabled(c *C) { 4817 s.state.Lock() 4818 defer s.state.Unlock() 4819 4820 snapstate.Set(s.state, "core", &snapstate.SnapState{ 4821 Active: true, 4822 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "beta"}}, 4823 Current: snap.R(1), 4824 SnapType: "os", 4825 }) 4826 tr := config.NewTransaction(s.state) 4827 tr.Set("core", "experimental.snapd-snap", true) 4828 tr.Commit() 4829 4830 s.state.Unlock() 4831 defer s.se.Stop() 4832 s.settle(c) 4833 s.state.Lock() 4834 4835 c.Check(s.state.Changes(), HasLen, 1) 4836 chg := s.state.Changes()[0] 4837 c.Check(chg.Kind(), Equals, "transition-to-snapd-snap") 4838 c.Assert(chg.Err(), IsNil) 4839 c.Assert(chg.IsReady(), Equals, true) 4840 4841 // snapd snap is instaleld from the default channel 4842 var snapst snapstate.SnapState 4843 snapstate.Get(s.state, "snapd", &snapst) 4844 c.Assert(snapst.TrackingChannel, Equals, "latest/stable") 4845 } 4846 4847 func (s *snapmgrTestSuite) TestTransitionSnapdSnapWithCoreRunthrough(c *C) { 4848 s.state.Lock() 4849 defer s.state.Unlock() 4850 4851 snapstate.Set(s.state, "core", &snapstate.SnapState{ 4852 Active: true, 4853 Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "edge"}}, 4854 Current: snap.R(1), 4855 SnapType: "os", 4856 // TrackingChannel 4857 TrackingChannel: "latest/beta", 4858 }) 4859 tr := config.NewTransaction(s.state) 4860 tr.Set("core", "experimental.snapd-snap", true) 4861 tr.Commit() 4862 4863 s.state.Unlock() 4864 defer s.se.Stop() 4865 s.settle(c) 4866 s.state.Lock() 4867 4868 c.Assert(s.state.Changes(), HasLen, 1) 4869 chg := s.state.Changes()[0] 4870 c.Assert(chg.Kind(), Equals, "transition-to-snapd-snap") 4871 c.Assert(chg.Err(), IsNil) 4872 c.Assert(chg.IsReady(), Equals, true) 4873 c.Check(s.fakeStore.downloads, HasLen, 1) 4874 ts := state.NewTaskSet(chg.Tasks()...) 4875 verifyInstallTasks(c, noConfigure, 0, ts, s.state) 4876 4877 // ensure preferences from the core snap got transferred over 4878 var snapst snapstate.SnapState 4879 snapstate.Get(s.state, "snapd", &snapst) 4880 c.Assert(snapst.TrackingChannel, Equals, "latest/beta") 4881 } 4882 4883 func (s *snapmgrTestSuite) TestTransitionSnapdSnapTimeLimitWorks(c *C) { 4884 s.state.Lock() 4885 defer s.state.Unlock() 4886 4887 tr := config.NewTransaction(s.state) 4888 tr.Set("core", "experimental.snapd-snap", true) 4889 tr.Commit() 4890 4891 // tried 3h ago, no retry 4892 s.state.Set("snapd-transition-last-retry-time", time.Now().Add(-3*time.Hour)) 4893 4894 s.state.Unlock() 4895 defer s.se.Stop() 4896 s.settle(c) 4897 s.state.Lock() 4898 4899 c.Check(s.state.Changes(), HasLen, 0) 4900 4901 // tried 7h ago, retry 4902 s.state.Set("snapd-transition-last-retry-time", time.Now().Add(-7*time.Hour)) 4903 4904 s.state.Unlock() 4905 defer s.se.Stop() 4906 s.settle(c) 4907 s.state.Lock() 4908 c.Check(s.state.Changes(), HasLen, 1) 4909 4910 var t time.Time 4911 s.state.Get("snapd-transition-last-retry-time", &t) 4912 c.Assert(time.Since(t) < 2*time.Minute, Equals, true) 4913 } 4914 4915 type unhappyStore struct { 4916 *fakeStore 4917 } 4918 4919 func (s unhappyStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) { 4920 if assertQuery != nil { 4921 panic("no assertion query support") 4922 } 4923 4924 return nil, nil, fmt.Errorf("a grumpy store") 4925 } 4926 4927 func (s *snapmgrTestSuite) TestTransitionSnapdSnapError(c *C) { 4928 s.state.Lock() 4929 defer s.state.Unlock() 4930 4931 snapstate.ReplaceStore(s.state, unhappyStore{fakeStore: s.fakeStore}) 4932 4933 tr := config.NewTransaction(s.state) 4934 tr.Set("core", "experimental.snapd-snap", true) 4935 tr.Commit() 4936 4937 s.state.Unlock() 4938 defer s.se.Stop() 4939 err := s.o.Settle(5 * time.Second) 4940 c.Assert(err, ErrorMatches, `state ensure errors: \[a grumpy store\]`) 4941 4942 s.state.Lock() 4943 c.Check(s.state.Changes(), HasLen, 0) 4944 4945 // all the attempts were recorded 4946 var t time.Time 4947 s.state.Get("snapd-transition-last-retry-time", &t) 4948 c.Assert(time.Since(t) < 2*time.Minute, Equals, true) 4949 4950 var cnt int 4951 s.state.Get("snapd-transition-retry", &cnt) 4952 c.Assert(cnt, Equals, 1) 4953 4954 // the transition is not tried again (because of retry time) 4955 s.state.Unlock() 4956 err = s.o.Settle(5 * time.Second) 4957 c.Assert(err, IsNil) 4958 s.state.Lock() 4959 4960 s.state.Get("snapd-transition-retry", &cnt) 4961 c.Assert(cnt, Equals, 1) 4962 } 4963 4964 func (s *snapmgrTestSuite) TestTransitionSnapdSnapBlocksOtherChanges(c *C) { 4965 s.state.Lock() 4966 defer s.state.Unlock() 4967 4968 // if we have a snapd transition 4969 chg := s.state.NewChange("transition-to-snapd-snap", "...") 4970 chg.SetStatus(state.DoStatus) 4971 4972 // other tasks block until the transition is done 4973 _, err := snapstate.Install(context.Background(), s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{}) 4974 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 4975 c.Check(err, ErrorMatches, "transition to snapd snap in progress, no other changes allowed until this is done") 4976 4977 // and when the transition is done, other tasks run 4978 chg.SetStatus(state.DoneStatus) 4979 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{}) 4980 c.Check(err, IsNil) 4981 c.Check(ts, NotNil) 4982 } 4983 4984 func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsForUbuntuCore(c *C) { 4985 s.checkForceDevModeCleanupRuns(c, "ubuntu-core", true) 4986 } 4987 4988 func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsForCore(c *C) { 4989 s.checkForceDevModeCleanupRuns(c, "core", true) 4990 } 4991 4992 func (s *snapmgrTestSuite) TestForceDevModeCleanupSkipsRando(c *C) { 4993 s.checkForceDevModeCleanupRuns(c, "rando", false) 4994 } 4995 4996 func (s *snapmgrTestSuite) checkForceDevModeCleanupRuns(c *C, name string, shouldBeReset bool) { 4997 r := sandbox.MockForceDevMode(true) 4998 defer r() 4999 c.Assert(sandbox.ForceDevMode(), Equals, true) 5000 5001 s.state.Lock() 5002 defer s.state.Unlock() 5003 5004 snapstate.Set(s.state, name, &snapstate.SnapState{ 5005 Active: true, 5006 Sequence: []*snap.SideInfo{{ 5007 RealName: name, 5008 SnapID: "id-id-id", 5009 Revision: snap.R(1)}}, 5010 Current: snap.R(1), 5011 SnapType: "os", 5012 Flags: snapstate.Flags{DevMode: true}, 5013 }) 5014 5015 var snapst1 snapstate.SnapState 5016 // sanity check 5017 snapstate.Get(s.state, name, &snapst1) 5018 c.Assert(snapst1.DevMode, Equals, true) 5019 5020 s.state.Unlock() 5021 defer s.se.Stop() 5022 s.settle(c) 5023 s.state.Lock() 5024 5025 var snapst2 snapstate.SnapState 5026 snapstate.Get(s.state, name, &snapst2) 5027 5028 c.Check(snapst2.DevMode, Equals, !shouldBeReset) 5029 5030 var n int 5031 s.state.Get("fix-forced-devmode", &n) 5032 c.Check(n, Equals, 1) 5033 } 5034 5035 func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsNoSnaps(c *C) { 5036 r := sandbox.MockForceDevMode(true) 5037 defer r() 5038 c.Assert(sandbox.ForceDevMode(), Equals, true) 5039 5040 defer s.se.Stop() 5041 s.settle(c) 5042 s.state.Lock() 5043 defer s.state.Unlock() 5044 5045 var n int 5046 s.state.Get("fix-forced-devmode", &n) 5047 c.Check(n, Equals, 1) 5048 } 5049 5050 func (s *snapmgrTestSuite) TestForceDevModeCleanupSkipsNonForcedOS(c *C) { 5051 r := sandbox.MockForceDevMode(false) 5052 defer r() 5053 c.Assert(sandbox.ForceDevMode(), Equals, false) 5054 5055 s.state.Lock() 5056 defer s.state.Unlock() 5057 5058 snapstate.Set(s.state, "core", &snapstate.SnapState{ 5059 Active: true, 5060 Sequence: []*snap.SideInfo{{ 5061 RealName: "core", 5062 SnapID: "id-id-id", 5063 Revision: snap.R(1)}}, 5064 Current: snap.R(1), 5065 SnapType: "os", 5066 Flags: snapstate.Flags{DevMode: true}, 5067 }) 5068 5069 var snapst1 snapstate.SnapState 5070 // sanity check 5071 snapstate.Get(s.state, "core", &snapst1) 5072 c.Assert(snapst1.DevMode, Equals, true) 5073 5074 s.state.Unlock() 5075 defer s.se.Stop() 5076 s.settle(c) 5077 s.state.Lock() 5078 5079 var snapst2 snapstate.SnapState 5080 snapstate.Get(s.state, "core", &snapst2) 5081 5082 // no change 5083 c.Check(snapst2.DevMode, Equals, true) 5084 5085 // not really run at all in fact 5086 var n int 5087 s.state.Get("fix-forced-devmode", &n) 5088 c.Check(n, Equals, 0) 5089 } 5090 5091 func (s *snapmgrTestSuite) TestEnsureAliasesV2(c *C) { 5092 s.state.Lock() 5093 defer s.state.Unlock() 5094 5095 snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) { 5096 switch info.InstanceName() { 5097 case "alias-snap": 5098 return map[string]string{ 5099 "alias1": "cmd1", 5100 "alias2": "cmd2", 5101 }, nil 5102 } 5103 return nil, nil 5104 } 5105 5106 snapstate.Set(s.state, "core", nil) 5107 snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{ 5108 Sequence: []*snap.SideInfo{ 5109 {RealName: "alias-snap", Revision: snap.R(11)}, 5110 }, 5111 Current: snap.R(11), 5112 Active: true, 5113 }) 5114 5115 s.state.Set("aliases", map[string]map[string]string{ 5116 "alias-snap": { 5117 "alias1": "auto", 5118 }, 5119 }) 5120 5121 s.state.Unlock() 5122 err := s.snapmgr.Ensure() 5123 s.state.Lock() 5124 c.Assert(err, IsNil) 5125 5126 var gone interface{} 5127 err = s.state.Get("aliases", &gone) 5128 c.Assert(err, Equals, state.ErrNoState) 5129 5130 var snapst snapstate.SnapState 5131 err = snapstate.Get(s.state, "alias-snap", &snapst) 5132 c.Assert(err, IsNil) 5133 5134 c.Check(snapst.AutoAliasesDisabled, Equals, false) 5135 c.Check(snapst.AliasesPending, Equals, false) 5136 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 5137 "alias1": {Auto: "cmd1"}, 5138 "alias2": {Auto: "cmd2"}, 5139 }) 5140 5141 expected := fakeOps{ 5142 { 5143 op: "remove-snap-aliases", 5144 name: "alias-snap", 5145 }, 5146 { 5147 op: "update-aliases", 5148 aliases: []*backend.Alias{ 5149 {Name: "alias1", Target: "alias-snap.cmd1"}, 5150 {Name: "alias2", Target: "alias-snap.cmd2"}, 5151 }, 5152 }, 5153 } 5154 // start with an easier-to-read error if this fails: 5155 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 5156 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 5157 } 5158 5159 func (s *snapmgrTestSuite) TestEnsureAliasesV2SnapDisabled(c *C) { 5160 s.state.Lock() 5161 defer s.state.Unlock() 5162 5163 snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) { 5164 switch info.InstanceName() { 5165 case "alias-snap": 5166 return map[string]string{ 5167 "alias1": "cmd1", 5168 "alias2": "cmd2", 5169 }, nil 5170 } 5171 return nil, nil 5172 } 5173 5174 snapstate.Set(s.state, "core", nil) 5175 snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{ 5176 Sequence: []*snap.SideInfo{ 5177 {RealName: "alias-snap", Revision: snap.R(11)}, 5178 }, 5179 Current: snap.R(11), 5180 Active: false, 5181 }) 5182 5183 s.state.Set("aliases", map[string]map[string]string{ 5184 "alias-snap": { 5185 "alias1": "auto", 5186 }, 5187 }) 5188 5189 s.state.Unlock() 5190 err := s.snapmgr.Ensure() 5191 s.state.Lock() 5192 c.Assert(err, IsNil) 5193 5194 var gone interface{} 5195 err = s.state.Get("aliases", &gone) 5196 c.Assert(err, Equals, state.ErrNoState) 5197 5198 var snapst snapstate.SnapState 5199 err = snapstate.Get(s.state, "alias-snap", &snapst) 5200 c.Assert(err, IsNil) 5201 5202 c.Check(snapst.AutoAliasesDisabled, Equals, false) 5203 c.Check(snapst.AliasesPending, Equals, true) 5204 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 5205 "alias1": {Auto: "cmd1"}, 5206 "alias2": {Auto: "cmd2"}, 5207 }) 5208 5209 expected := fakeOps{ 5210 { 5211 op: "remove-snap-aliases", 5212 name: "alias-snap", 5213 }, 5214 } 5215 // start with an easier-to-read error if this fails: 5216 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 5217 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 5218 } 5219 5220 func (s *snapmgrTestSuite) TestEnsureAliasesV2MarkAliasTasksInError(c *C) { 5221 s.state.Lock() 5222 defer s.state.Unlock() 5223 5224 s.state.Set("aliases", map[string]map[string]string{ 5225 "alias-snap": { 5226 "alias1": "auto", 5227 }, 5228 }) 5229 5230 // pending old alias task 5231 t := s.state.NewTask("alias", "...") 5232 t.Set("aliases", map[string]string{}) 5233 chg := s.state.NewChange("alias chg", "...") 5234 chg.AddTask(t) 5235 5236 s.state.Unlock() 5237 err := s.snapmgr.Ensure() 5238 s.state.Lock() 5239 c.Assert(err, IsNil) 5240 5241 c.Check(chg.Status(), Equals, state.ErrorStatus) 5242 c.Check(chg.IsReady(), Equals, true) 5243 c.Check(t.Status(), Equals, state.ErrorStatus) 5244 } 5245 5246 func (s *snapmgrTestSuite) TestConflictMany(c *C) { 5247 s.state.Lock() 5248 defer s.state.Unlock() 5249 5250 for _, instanceName := range []string{"a-snap", "b-snap"} { 5251 snapstate.Set(s.state, instanceName, &snapstate.SnapState{ 5252 Sequence: []*snap.SideInfo{ 5253 {RealName: instanceName, Revision: snap.R(11)}, 5254 }, 5255 Current: snap.R(11), 5256 Active: false, 5257 }) 5258 5259 ts, err := snapstate.Enable(s.state, instanceName) 5260 c.Assert(err, IsNil) 5261 // need a change to make the tasks visible 5262 s.state.NewChange("enable", "...").AddAll(ts) 5263 } 5264 5265 // things that should be ok: 5266 for _, m := range [][]string{ 5267 {}, //nothing 5268 {"c-snap"}, 5269 {"c-snap", "d-snap", "e-snap", "f-snap"}, 5270 } { 5271 c.Check(snapstate.CheckChangeConflictMany(s.state, m, ""), IsNil) 5272 } 5273 5274 // things that should not be ok: 5275 for _, m := range [][]string{ 5276 {"a-snap"}, 5277 {"a-snap", "b-snap"}, 5278 {"a-snap", "c-snap"}, 5279 {"b-snap", "c-snap"}, 5280 } { 5281 err := snapstate.CheckChangeConflictMany(s.state, m, "") 5282 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 5283 c.Check(err, ErrorMatches, `snap "[^"]*" has "enable" change in progress`) 5284 } 5285 } 5286 5287 func (s *snapmgrTestSuite) TestConflictRemodeling(c *C) { 5288 s.state.Lock() 5289 defer s.state.Unlock() 5290 5291 chg := s.state.NewChange("remodel", "...") 5292 chg.SetStatus(state.DoingStatus) 5293 5294 err := snapstate.CheckChangeConflictMany(s.state, []string{"a-snap"}, "") 5295 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 5296 c.Check(err, ErrorMatches, `remodeling in progress, no other changes allowed until this is done`) 5297 5298 // a remodel conflicts with another remodel 5299 err = snapstate.CheckChangeConflictRunExclusively(s.state, "remodel") 5300 c.Check(err, ErrorMatches, `remodeling in progress, no other changes allowed until this is done`) 5301 5302 // we have a remodel change in state, a remodel change triggers are conflict 5303 err = snapstate.CheckChangeConflictRunExclusively(s.state, "create-recovery-system") 5304 c.Check(err, ErrorMatches, `remodeling in progress, no other changes allowed until this is done`) 5305 } 5306 5307 func (s *snapmgrTestSuite) TestConflictCreateRecovery(c *C) { 5308 s.state.Lock() 5309 defer s.state.Unlock() 5310 5311 chg := s.state.NewChange("create-recovery-system", "...") 5312 c.Check(chg.IsReady(), Equals, false) 5313 chg.SetStatus(state.DoingStatus) 5314 5315 err := snapstate.CheckChangeConflictMany(s.state, []string{"a-snap"}, "") 5316 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 5317 c.Check(err, ErrorMatches, `creating recovery system in progress, no other changes allowed until this is done`) 5318 5319 // remodeling conflicts with a change that creates a recovery system 5320 err = snapstate.CheckChangeConflictRunExclusively(s.state, "remodel") 5321 c.Check(err, ErrorMatches, `creating recovery system in progress, no other changes allowed until this is done`) 5322 5323 // so does another another create recovery system change 5324 err = snapstate.CheckChangeConflictRunExclusively(s.state, "create-recovery-system") 5325 c.Check(err, ErrorMatches, `creating recovery system in progress, no other changes allowed until this is done`) 5326 } 5327 5328 func (s *snapmgrTestSuite) TestConflictExclusive(c *C) { 5329 s.state.Lock() 5330 defer s.state.Unlock() 5331 5332 chg := s.state.NewChange("install-snap-a", "...") 5333 chg.SetStatus(state.DoingStatus) 5334 5335 // a remodel conflicts with any other change 5336 err := snapstate.CheckChangeConflictRunExclusively(s.state, "remodel") 5337 c.Check(err, ErrorMatches, `other changes in progress \(conflicting change "install-snap-a"\), change "remodel" not allowed until they are done`) 5338 // and so does the remodel conflicts with any other change 5339 err = snapstate.CheckChangeConflictRunExclusively(s.state, "create-recovery-system") 5340 c.Check(err, ErrorMatches, `other changes in progress \(conflicting change "install-snap-a"\), change "create-recovery-system" not allowed until they are done`) 5341 } 5342 5343 type contentStore struct { 5344 *fakeStore 5345 state *state.State 5346 } 5347 5348 func (s contentStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) { 5349 sars, _, err := s.fakeStore.SnapAction(ctx, currentSnaps, actions, assertQuery, user, opts) 5350 if err != nil { 5351 return nil, nil, err 5352 } 5353 if len(sars) != 1 { 5354 panic("expected to be queried for install of only one snap at a time") 5355 } 5356 info := sars[0].Info 5357 switch info.InstanceName() { 5358 case "snap-content-plug": 5359 info.Plugs = map[string]*snap.PlugInfo{ 5360 "some-plug": { 5361 Snap: info, 5362 Name: "shared-content", 5363 Interface: "content", 5364 Attrs: map[string]interface{}{ 5365 "default-provider": "snap-content-slot", 5366 "content": "shared-content", 5367 }, 5368 }, 5369 } 5370 case "snap-content-plug-compat": 5371 info.Plugs = map[string]*snap.PlugInfo{ 5372 "some-plug": { 5373 Snap: info, 5374 Name: "shared-content", 5375 Interface: "content", 5376 Attrs: map[string]interface{}{ 5377 "default-provider": "snap-content-slot:some-slot", 5378 "content": "shared-content", 5379 }, 5380 }, 5381 } 5382 case "snap-content-slot": 5383 info.Slots = map[string]*snap.SlotInfo{ 5384 "some-slot": { 5385 Snap: info, 5386 Name: "shared-content", 5387 Interface: "content", 5388 Attrs: map[string]interface{}{ 5389 "content": "shared-content", 5390 }, 5391 }, 5392 } 5393 case "snap-content-circular1": 5394 info.Plugs = map[string]*snap.PlugInfo{ 5395 "circular-plug1": { 5396 Snap: info, 5397 Name: "circular-plug1", 5398 Interface: "content", 5399 Attrs: map[string]interface{}{ 5400 "default-provider": "snap-content-circular2", 5401 "content": "circular2", 5402 }, 5403 }, 5404 } 5405 info.Slots = map[string]*snap.SlotInfo{ 5406 "circular-slot1": { 5407 Snap: info, 5408 Name: "circular-slot1", 5409 Interface: "content", 5410 Attrs: map[string]interface{}{ 5411 "content": "circular1", 5412 }, 5413 }, 5414 } 5415 case "snap-content-circular2": 5416 info.Plugs = map[string]*snap.PlugInfo{ 5417 "circular-plug2": { 5418 Snap: info, 5419 Name: "circular-plug2", 5420 Interface: "content", 5421 Attrs: map[string]interface{}{ 5422 "default-provider": "snap-content-circular1", 5423 "content": "circular2", 5424 }, 5425 }, 5426 } 5427 info.Slots = map[string]*snap.SlotInfo{ 5428 "circular-slot2": { 5429 Snap: info, 5430 Name: "circular-slot2", 5431 Interface: "content", 5432 Attrs: map[string]interface{}{ 5433 "content": "circular1", 5434 }, 5435 }, 5436 } 5437 } 5438 5439 return []store.SnapActionResult{{Info: info}}, nil, err 5440 } 5441 5442 func (s *snapmgrTestSuite) TestSnapManagerLegacyRefreshSchedule(c *C) { 5443 s.state.Lock() 5444 defer s.state.Unlock() 5445 5446 for _, t := range []struct { 5447 in string 5448 out string 5449 legacy bool 5450 }{ 5451 {"", snapstate.DefaultRefreshSchedule, false}, 5452 {"invalid schedule", snapstate.DefaultRefreshSchedule, false}, 5453 {"8:00-12:00", "8:00-12:00", true}, 5454 // using the legacy configuration option with a new-style 5455 // refresh.timer string is rejected (i.e. the legacy parser is 5456 // used for the parsing) 5457 {"0:00~24:00/24", snapstate.DefaultRefreshSchedule, false}, 5458 } { 5459 if t.in != "" { 5460 tr := config.NewTransaction(s.state) 5461 tr.Set("core", "refresh.timer", "") 5462 tr.Set("core", "refresh.schedule", t.in) 5463 tr.Commit() 5464 } 5465 scheduleStr, legacy, err := s.snapmgr.RefreshSchedule() 5466 c.Check(err, IsNil) 5467 c.Check(scheduleStr, Equals, t.out) 5468 c.Check(legacy, Equals, t.legacy) 5469 } 5470 } 5471 5472 func (s *snapmgrTestSuite) TestSnapManagerRefreshSchedule(c *C) { 5473 s.state.Lock() 5474 defer s.state.Unlock() 5475 5476 for _, t := range []struct { 5477 in string 5478 out string 5479 }{ 5480 {"", snapstate.DefaultRefreshSchedule}, 5481 {"invalid schedule", snapstate.DefaultRefreshSchedule}, 5482 {"8:00-12:00", "8:00-12:00"}, 5483 // this is only valid under the new schedule parser 5484 {"9:00~15:00/2,,mon,20:00", "9:00~15:00/2,,mon,20:00"}, 5485 } { 5486 if t.in != "" { 5487 tr := config.NewTransaction(s.state) 5488 tr.Set("core", "refresh.timer", t.in) 5489 tr.Commit() 5490 } 5491 scheduleStr, legacy, err := s.snapmgr.RefreshSchedule() 5492 c.Check(err, IsNil) 5493 c.Check(scheduleStr, Equals, t.out) 5494 c.Check(legacy, Equals, false) 5495 } 5496 } 5497 5498 func (s *snapmgrTestSuite) TestParallelInstallValidateFeatureFlag(c *C) { 5499 s.state.Lock() 5500 defer s.state.Unlock() 5501 5502 info := &snap.Info{ 5503 InstanceKey: "foo", 5504 } 5505 5506 err := snapstate.ValidateFeatureFlags(s.state, info) 5507 c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`) 5508 5509 // various forms of disabling 5510 tr := config.NewTransaction(s.state) 5511 tr.Set("core", "experimental.parallel-instances", false) 5512 tr.Commit() 5513 5514 err = snapstate.ValidateFeatureFlags(s.state, info) 5515 c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`) 5516 5517 tr = config.NewTransaction(s.state) 5518 tr.Set("core", "experimental.parallel-instances", "") 5519 tr.Commit() 5520 5521 err = snapstate.ValidateFeatureFlags(s.state, info) 5522 c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`) 5523 5524 tr = config.NewTransaction(s.state) 5525 tr.Set("core", "experimental.parallel-instances", nil) 5526 tr.Commit() 5527 5528 err = snapstate.ValidateFeatureFlags(s.state, info) 5529 c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`) 5530 5531 tr = config.NewTransaction(s.state) 5532 tr.Set("core", "experimental.parallel-instances", "veryfalse") 5533 tr.Commit() 5534 5535 err = snapstate.ValidateFeatureFlags(s.state, info) 5536 c.Assert(err, ErrorMatches, `parallel-instances can only be set to 'true' or 'false', got "veryfalse"`) 5537 5538 // enable parallel instances 5539 tr = config.NewTransaction(s.state) 5540 tr.Set("core", "experimental.parallel-instances", true) 5541 tr.Commit() 5542 5543 err = snapstate.ValidateFeatureFlags(s.state, info) 5544 c.Assert(err, IsNil) 5545 } 5546 5547 func (s *snapmgrTestSuite) TestInjectTasks(c *C) { 5548 s.state.Lock() 5549 defer s.state.Unlock() 5550 5551 lane := s.state.NewLane() 5552 5553 // setup main task and two tasks waiting for it; all part of same change 5554 chg := s.state.NewChange("change", "") 5555 t0 := s.state.NewTask("task1", "") 5556 chg.AddTask(t0) 5557 t0.JoinLane(lane) 5558 t01 := s.state.NewTask("task1-1", "") 5559 t01.WaitFor(t0) 5560 chg.AddTask(t01) 5561 t02 := s.state.NewTask("task1-2", "") 5562 t02.WaitFor(t0) 5563 chg.AddTask(t02) 5564 5565 // setup extra tasks 5566 t1 := s.state.NewTask("task2", "") 5567 t2 := s.state.NewTask("task3", "") 5568 ts := state.NewTaskSet(t1, t2) 5569 5570 snapstate.InjectTasks(t0, ts) 5571 5572 // verify that extra tasks are now part of same change 5573 c.Assert(t1.Change().ID(), Equals, t0.Change().ID()) 5574 c.Assert(t2.Change().ID(), Equals, t0.Change().ID()) 5575 c.Assert(t1.Change().ID(), Equals, chg.ID()) 5576 5577 c.Assert(t1.Lanes(), DeepEquals, []int{lane}) 5578 5579 // verify that halt tasks of the main task now wait for extra tasks 5580 c.Assert(t1.HaltTasks(), HasLen, 2) 5581 c.Assert(t2.HaltTasks(), HasLen, 2) 5582 c.Assert(t1.HaltTasks(), DeepEquals, t2.HaltTasks()) 5583 5584 ids := []string{t1.HaltTasks()[0].Kind(), t2.HaltTasks()[1].Kind()} 5585 sort.Strings(ids) 5586 c.Assert(ids, DeepEquals, []string{"task1-1", "task1-2"}) 5587 5588 // verify that extra tasks wait for the main task 5589 c.Assert(t1.WaitTasks(), HasLen, 1) 5590 c.Assert(t1.WaitTasks()[0].Kind(), Equals, "task1") 5591 c.Assert(t2.WaitTasks(), HasLen, 1) 5592 c.Assert(t2.WaitTasks()[0].Kind(), Equals, "task1") 5593 } 5594 5595 func (s *snapmgrTestSuite) TestInjectTasksWithNullChange(c *C) { 5596 s.state.Lock() 5597 defer s.state.Unlock() 5598 5599 // setup main task 5600 t0 := s.state.NewTask("task1", "") 5601 t01 := s.state.NewTask("task1-1", "") 5602 t01.WaitFor(t0) 5603 5604 // setup extra task 5605 t1 := s.state.NewTask("task2", "") 5606 ts := state.NewTaskSet(t1) 5607 5608 snapstate.InjectTasks(t0, ts) 5609 5610 c.Assert(t1.Lanes(), DeepEquals, []int{0}) 5611 5612 // verify that halt tasks of the main task now wait for extra tasks 5613 c.Assert(t1.HaltTasks(), HasLen, 1) 5614 c.Assert(t1.HaltTasks()[0].Kind(), Equals, "task1-1") 5615 } 5616 5617 func hasConfigureTask(ts *state.TaskSet) bool { 5618 for _, tk := range taskKinds(ts.Tasks()) { 5619 if tk == "run-hook[configure]" { 5620 return true 5621 } 5622 } 5623 return false 5624 } 5625 5626 func (s *snapmgrTestSuite) TestNoConfigureForBasesTask(c *C) { 5627 s.state.Lock() 5628 defer s.state.Unlock() 5629 5630 // normal snaps get a configure task 5631 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 5632 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 5633 c.Assert(err, IsNil) 5634 c.Check(hasConfigureTask(ts), Equals, true) 5635 5636 // but bases do not for install 5637 ts, err = snapstate.Install(context.Background(), s.state, "some-base", opts, s.user.ID, snapstate.Flags{}) 5638 c.Assert(err, IsNil) 5639 c.Check(hasConfigureTask(ts), Equals, false) 5640 5641 // or for refresh 5642 snapstate.Set(s.state, "some-base", &snapstate.SnapState{ 5643 Active: true, 5644 TrackingChannel: "latest/edge", 5645 Sequence: []*snap.SideInfo{{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)}}, 5646 Current: snap.R(1), 5647 SnapType: "base", 5648 }) 5649 ts, err = snapstate.Update(s.state, "some-base", nil, s.user.ID, snapstate.Flags{}) 5650 c.Assert(err, IsNil) 5651 c.Check(hasConfigureTask(ts), Equals, false) 5652 } 5653 5654 func (s *snapmgrTestSuite) TestSnapdSnapOnCoreWithoutBase(c *C) { 5655 s.state.Lock() 5656 defer s.state.Unlock() 5657 r := release.MockOnClassic(false) 5658 defer r() 5659 5660 // it is now possible to install snapd snap on a system with core 5661 _, err := snapstate.Install(context.Background(), s.state, "snapd", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 5662 c.Assert(err, IsNil) 5663 } 5664 5665 func (s *snapmgrTestSuite) TestSnapdSnapOnSystemsWithoutBaseOnUbuntuCore(c *C) { 5666 s.state.Lock() 5667 defer s.state.Unlock() 5668 r := release.MockOnClassic(false) 5669 defer r() 5670 5671 // it is not possible to opt-into the snapd snap on core yet 5672 tr := config.NewTransaction(s.state) 5673 tr.Set("core", "experimental.snapd-snap", true) 5674 tr.Commit() 5675 5676 // it is now possible to install snapd snap on a system with core, experimental option has no effect 5677 _, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{}) 5678 c.Assert(err, IsNil) 5679 } 5680 5681 func (s *snapmgrTestSuite) TestNoSnapdSnapOnSystemsWithoutBaseButOption(c *C) { 5682 s.state.Lock() 5683 defer s.state.Unlock() 5684 5685 tr := config.NewTransaction(s.state) 5686 tr.Set("core", "experimental.snapd-snap", true) 5687 tr.Commit() 5688 5689 _, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{}) 5690 c.Assert(err, IsNil) 5691 } 5692 5693 func (s *snapmgrTestSuite) TestNoConfigureForSnapdSnap(c *C) { 5694 s.state.Lock() 5695 defer s.state.Unlock() 5696 5697 // snapd cannot be installed unless the model uses a base snap 5698 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 5699 defer r() 5700 5701 // but snapd do not for install 5702 ts, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{}) 5703 c.Assert(err, IsNil) 5704 c.Check(hasConfigureTask(ts), Equals, false) 5705 5706 // or for refresh 5707 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 5708 Active: true, 5709 TrackingChannel: "latest/edge", 5710 Sequence: []*snap.SideInfo{{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)}}, 5711 Current: snap.R(1), 5712 SnapType: "app", 5713 }) 5714 ts, err = snapstate.Update(s.state, "snapd", nil, s.user.ID, snapstate.Flags{}) 5715 c.Assert(err, IsNil) 5716 c.Check(hasConfigureTask(ts), Equals, false) 5717 5718 } 5719 5720 func (s *snapmgrTestSuite) TestCanLoadOldSnapSetupWithoutType(c *C) { 5721 // ensure we don't crash when loading a SnapSetup json without 5722 // a type set 5723 oldSnapSetup := []byte(`{ 5724 "snap-path":"/some/path", 5725 "side-info": { 5726 "channel": "edge", 5727 "name": "some-snap", 5728 "revision": "1", 5729 "snap-id": "some-snap-id" 5730 } 5731 }`) 5732 var snapsup snapstate.SnapSetup 5733 err := json.Unmarshal(oldSnapSetup, &snapsup) 5734 c.Assert(err, IsNil) 5735 c.Check(snapsup.SnapPath, Equals, "/some/path") 5736 c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 5737 Channel: "edge", 5738 RealName: "some-snap", 5739 Revision: snap.R(1), 5740 SnapID: "some-snap-id", 5741 }) 5742 c.Check(snapsup.Type, Equals, snap.Type("")) 5743 } 5744 5745 func (s *snapmgrTestSuite) TestHasOtherInstances(c *C) { 5746 s.state.Lock() 5747 defer s.state.Unlock() 5748 5749 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 5750 Active: true, 5751 Sequence: []*snap.SideInfo{ 5752 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 5753 }, 5754 Current: snap.R(1), 5755 SnapType: "app", 5756 }) 5757 snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{ 5758 Active: true, 5759 Sequence: []*snap.SideInfo{ 5760 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 5761 }, 5762 Current: snap.R(3), 5763 SnapType: "app", 5764 InstanceKey: "instance", 5765 }) 5766 snapstate.Set(s.state, "some-other-snap", &snapstate.SnapState{ 5767 Active: true, 5768 Sequence: []*snap.SideInfo{ 5769 {RealName: "some-other-snap", SnapID: "some-other-snap-id", Revision: snap.R(1)}, 5770 }, 5771 Current: snap.R(1), 5772 SnapType: "app", 5773 }) 5774 5775 other, err := snapstate.HasOtherInstances(s.state, "some-snap") 5776 c.Assert(err, IsNil) 5777 c.Assert(other, Equals, true) 5778 other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance") 5779 c.Assert(err, IsNil) 5780 c.Assert(other, Equals, true) 5781 other, err = snapstate.HasOtherInstances(s.state, "some-other-snap") 5782 c.Assert(err, IsNil) 5783 c.Assert(other, Equals, false) 5784 // other snaps like only looks at the name of the refence snap 5785 other, err = snapstate.HasOtherInstances(s.state, "some-other-snap_instance") 5786 c.Assert(err, IsNil) 5787 c.Assert(other, Equals, true) 5788 5789 // remove the snap without instance key 5790 snapstate.Set(s.state, "some-snap", nil) 5791 // some-snap_instance is like some-snap 5792 other, err = snapstate.HasOtherInstances(s.state, "some-snap") 5793 c.Assert(err, IsNil) 5794 c.Assert(other, Equals, true) 5795 other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance") 5796 c.Assert(err, IsNil) 5797 c.Assert(other, Equals, false) 5798 5799 // add another snap with instance key 5800 snapstate.Set(s.state, "some-snap_other", &snapstate.SnapState{ 5801 Active: true, 5802 Sequence: []*snap.SideInfo{ 5803 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}, 5804 }, 5805 Current: snap.R(3), 5806 SnapType: "app", 5807 InstanceKey: "other", 5808 }) 5809 other, err = snapstate.HasOtherInstances(s.state, "some-snap") 5810 c.Assert(err, IsNil) 5811 c.Assert(other, Equals, true) 5812 other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance") 5813 c.Assert(err, IsNil) 5814 c.Assert(other, Equals, true) 5815 } 5816 5817 func (s *snapmgrTestSuite) TestRequestSalt(c *C) { 5818 si := snap.SideInfo{ 5819 RealName: "other-snap", 5820 Revision: snap.R(7), 5821 SnapID: "other-snap-id", 5822 } 5823 s.state.Lock() 5824 defer s.state.Unlock() 5825 5826 snapstate.Set(s.state, "other-snap", &snapstate.SnapState{ 5827 Active: true, 5828 Sequence: []*snap.SideInfo{&si}, 5829 Current: si.Revision, 5830 SnapType: "app", 5831 }) 5832 snapstate.Set(s.state, "other-snap_instance", &snapstate.SnapState{ 5833 Active: true, 5834 Sequence: []*snap.SideInfo{&si}, 5835 Current: si.Revision, 5836 SnapType: "app", 5837 InstanceKey: "instance", 5838 }) 5839 5840 // clear request-salt to have it generated 5841 s.state.Set("refresh-privacy-key", nil) 5842 5843 _, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, s.user.ID, snapstate.Flags{}) 5844 c.Assert(err, ErrorMatches, "internal error: request salt is unset") 5845 5846 s.state.Set("refresh-privacy-key", "privacy-key") 5847 5848 chg := s.state.NewChange("install", "install a snap") 5849 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, s.user.ID, snapstate.Flags{}) 5850 c.Assert(err, IsNil) 5851 chg.AddAll(ts) 5852 5853 s.state.Unlock() 5854 defer s.se.Stop() 5855 s.settle(c) 5856 s.state.Lock() 5857 5858 c.Assert(len(s.fakeBackend.ops) >= 1, Equals, true) 5859 storeAction := s.fakeBackend.ops[0] 5860 c.Assert(storeAction.op, Equals, "storesvc-snap-action") 5861 c.Assert(storeAction.curSnaps, HasLen, 2) 5862 c.Assert(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true) 5863 } 5864 5865 type canDisableSuite struct{} 5866 5867 var _ = Suite(&canDisableSuite{}) 5868 5869 func (s *canDisableSuite) TestCanDisable(c *C) { 5870 for _, tt := range []struct { 5871 typ snap.Type 5872 canDisable bool 5873 }{ 5874 {snap.TypeApp, true}, 5875 {snap.TypeGadget, false}, 5876 {snap.TypeKernel, false}, 5877 {snap.TypeOS, false}, 5878 } { 5879 info := &snap.Info{SnapType: tt.typ} 5880 c.Check(snapstate.CanDisable(info), Equals, tt.canDisable) 5881 } 5882 } 5883 5884 func (s *snapmgrTestSuite) TestGadgetConnections(c *C) { 5885 r := release.MockOnClassic(false) 5886 defer r() 5887 5888 // using MockSnap, we want to read the bits on disk 5889 snapstate.MockSnapReadInfo(snap.ReadInfo) 5890 5891 deviceCtxNoGadget := deviceWithoutGadgetContext() 5892 deviceCtx := deviceWithGadgetContext("the-gadget") 5893 5894 s.state.Lock() 5895 defer s.state.Unlock() 5896 5897 _, err := snapstate.GadgetConnections(s.state, deviceCtxNoGadget) 5898 c.Assert(err, Equals, state.ErrNoState) 5899 5900 _, err = snapstate.GadgetConnections(s.state, deviceCtx) 5901 c.Assert(err, Equals, state.ErrNoState) 5902 5903 s.prepareGadget(c, ` 5904 connections: 5905 - plug: snap1idididididididididididididi:plug 5906 slot: snap2idididididididididididididi:slot 5907 `) 5908 5909 conns, err := snapstate.GadgetConnections(s.state, deviceCtx) 5910 c.Assert(err, IsNil) 5911 c.Check(conns, DeepEquals, []gadget.Connection{ 5912 {Plug: gadget.ConnectionPlug{SnapID: "snap1idididididididididididididi", Plug: "plug"}, Slot: gadget.ConnectionSlot{SnapID: "snap2idididididididididididididi", Slot: "slot"}}}) 5913 } 5914 5915 func (s *snapmgrTestSuite) TestGadgetConnectionsUC20(c *C) { 5916 r := release.MockOnClassic(false) 5917 defer r() 5918 5919 // using MockSnap, we want to read the bits on disk 5920 snapstate.MockSnapReadInfo(snap.ReadInfo) 5921 5922 // use a UC20 model context 5923 deviceCtx := deviceWithGadgetContext20("the-gadget") 5924 5925 s.state.Lock() 5926 defer s.state.Unlock() 5927 5928 // provide a uc20 gadget structure 5929 s.prepareGadget(c, ` 5930 bootloader: grub 5931 structure: 5932 - name: ubuntu-seed 5933 role: system-seed 5934 filesystem: vfat 5935 type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B 5936 size: 1200M 5937 - name: ubuntu-boot 5938 role: system-boot 5939 filesystem: ext4 5940 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 5941 # whats the appropriate size? 5942 size: 750M 5943 - name: ubuntu-data 5944 role: system-data 5945 filesystem: ext4 5946 type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4 5947 size: 1G 5948 connections: 5949 - plug: snap1idididididididididididididi:plug 5950 slot: snap2idididididididididididididi:slot 5951 `) 5952 5953 conns, err := snapstate.GadgetConnections(s.state, deviceCtx) 5954 c.Assert(err, IsNil) 5955 c.Check(conns, DeepEquals, []gadget.Connection{ 5956 {Plug: gadget.ConnectionPlug{SnapID: "snap1idididididididididididididi", Plug: "plug"}, Slot: gadget.ConnectionSlot{SnapID: "snap2idididididididididididididi", Slot: "slot"}}}) 5957 } 5958 5959 func (s *snapmgrTestSuite) TestSnapManagerCanStandby(c *C) { 5960 s.state.Lock() 5961 defer s.state.Unlock() 5962 5963 // no snaps -> can standby 5964 s.state.Set("snaps", nil) 5965 c.Assert(s.snapmgr.CanStandby(), Equals, true) 5966 5967 // snaps installed -> can *not* standby 5968 snapstate.Set(s.state, "core", &snapstate.SnapState{ 5969 Active: true, 5970 Sequence: []*snap.SideInfo{ 5971 {RealName: "core", Revision: snap.R(1)}, 5972 }, 5973 Current: snap.R(1), 5974 SnapType: "os", 5975 }) 5976 c.Assert(s.snapmgr.CanStandby(), Equals, false) 5977 } 5978 5979 func (s *snapmgrTestSuite) TestResolveChannelPinnedTrack(c *C) { 5980 type test struct { 5981 snap string 5982 cur string 5983 new string 5984 exp string 5985 kernelTrack string 5986 gadgetTrack string 5987 err string 5988 } 5989 5990 for i, tc := range []test{ 5991 // neither kernel nor gadget 5992 {snap: "some-snap"}, 5993 {snap: "some-snap", new: "stable", exp: "stable"}, 5994 {snap: "some-snap", new: "foo/stable", exp: "foo/stable"}, 5995 {snap: "some-snap", new: "stable/with-branch", exp: "stable/with-branch"}, 5996 {snap: "some-snap", new: "supertrack/stable", exp: "supertrack/stable"}, 5997 {snap: "some-snap", new: "supertrack/stable/with-branch", exp: "supertrack/stable/with-branch"}, 5998 // kernel or gadget snap set, but unrelated snap 5999 {snap: "some-snap", new: "stable", exp: "stable", kernelTrack: "18"}, 6000 {snap: "some-snap", new: "foo/stable", exp: "foo/stable", kernelTrack: "18"}, 6001 {snap: "some-snap", new: "foo/stable", exp: "foo/stable", gadgetTrack: "18"}, 6002 // no pinned track 6003 {snap: "kernel", new: "latest/stable", exp: "latest/stable"}, 6004 {snap: "kernel", new: "stable", exp: "stable"}, 6005 {snap: "brand-gadget", new: "stable", exp: "stable"}, 6006 // not a risk only request 6007 {snap: "kernel", new: "", kernelTrack: "18"}, 6008 {snap: "brand-gadget", new: "", gadgetTrack: "18"}, 6009 {snap: "kernel", new: "latest/stable", kernelTrack: "18", err: "cannot switch from kernel track.*"}, 6010 {snap: "kernel", new: "latest/stable/hotfix-123", kernelTrack: "18", err: "cannot switch from kernel track.*"}, 6011 {snap: "kernel", new: "foo/stable", kernelTrack: "18", err: "cannot switch from kernel track.*"}, 6012 {snap: "brand-gadget", new: "foo/stable", exp: "18/stable", gadgetTrack: "18", err: "cannot switch from gadget track.*"}, 6013 {snap: "kernel", new: "18/stable", exp: "18/stable", kernelTrack: "18"}, 6014 {snap: "kernel", new: "18/stable", exp: "18/stable"}, 6015 {snap: "brand-gadget", new: "18/stable", exp: "18/stable", gadgetTrack: "18"}, 6016 {snap: "brand-gadget", new: "18/stable", exp: "18/stable"}, 6017 // risk/branch within a track 6018 {snap: "kernel", new: "stable/hotfix-123", exp: "18/stable/hotfix-123", kernelTrack: "18"}, 6019 {snap: "kernel", new: "18/stable/hotfix-123", exp: "18/stable/hotfix-123", kernelTrack: "18"}, 6020 // risk only defaults to pinned gadget track 6021 {snap: "brand-gadget", new: "stable", exp: "17/stable", gadgetTrack: "17"}, 6022 {snap: "brand-gadget", new: "edge", exp: "17/edge", gadgetTrack: "17"}, 6023 // risk only defaults to pinned kernel track 6024 {snap: "kernel", new: "stable", exp: "17/stable", kernelTrack: "17"}, 6025 {snap: "kernel", new: "edge", exp: "17/edge", kernelTrack: "17"}, 6026 // risk only defaults to current track 6027 {snap: "some-snap", new: "stable", cur: "stable", exp: "stable"}, 6028 {snap: "some-snap", new: "stable", cur: "latest/stable", exp: "latest/stable"}, 6029 {snap: "some-snap", new: "stable", cur: "sometrack/edge", exp: "sometrack/stable"}, 6030 } { 6031 if tc.kernelTrack != "" && tc.gadgetTrack != "" { 6032 c.Fatalf("%d: setting both kernel and gadget tracks is not supported by the test", i) 6033 } 6034 var model *asserts.Model 6035 switch { 6036 case tc.kernelTrack != "": 6037 model = ModelWithKernelTrack(tc.kernelTrack) 6038 case tc.gadgetTrack != "": 6039 model = ModelWithGadgetTrack(tc.gadgetTrack) 6040 default: 6041 model = DefaultModel() 6042 } 6043 deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model} 6044 s.state.Lock() 6045 ch, err := snapstate.ResolveChannel(s.state, tc.snap, tc.cur, tc.new, deviceCtx) 6046 s.state.Unlock() 6047 comment := Commentf("tc %d: %#v", i, tc) 6048 if tc.err != "" { 6049 c.Check(err, ErrorMatches, tc.err, comment) 6050 } else { 6051 c.Check(err, IsNil, comment) 6052 c.Check(ch, Equals, tc.exp, comment) 6053 } 6054 } 6055 } 6056 6057 func (s *snapmgrTestSuite) TestGadgetUpdateTaskAddedOnInstall(c *C) { 6058 restore := release.MockOnClassic(false) 6059 defer restore() 6060 6061 s.state.Lock() 6062 defer s.state.Unlock() 6063 6064 // task added on install 6065 ts, err := snapstate.Install(context.Background(), s.state, "brand-gadget", nil, 0, snapstate.Flags{}) 6066 c.Assert(err, IsNil) 6067 6068 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 6069 verifyInstallTasks(c, updatesGadget, 0, ts, s.state) 6070 } 6071 6072 func (s *snapmgrTestSuite) TestGadgetUpdateTaskAddedOnRefresh(c *C) { 6073 restore := release.MockOnClassic(false) 6074 defer restore() 6075 6076 s.state.Lock() 6077 defer s.state.Unlock() 6078 6079 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 6080 Active: true, 6081 Sequence: []*snap.SideInfo{ 6082 {RealName: "brand-gadget", SnapID: "brand-gadget-id", Revision: snap.R(1)}, 6083 }, 6084 Current: snap.R(1), 6085 SnapType: "gadget", 6086 }) 6087 6088 // and on update 6089 ts, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{}, 0, snapstate.Flags{}) 6090 c.Assert(err, IsNil) 6091 6092 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 6093 verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh|updatesGadget, 0, ts, s.state) 6094 6095 } 6096 6097 func (s *snapmgrTestSuite) TestForSnapSetupResetsFlags(c *C) { 6098 flags := snapstate.Flags{ 6099 DevMode: true, 6100 JailMode: true, 6101 Classic: true, 6102 TryMode: true, 6103 Revert: true, 6104 RemoveSnapPath: true, 6105 IgnoreValidation: true, 6106 Required: true, 6107 SkipConfigure: true, 6108 Unaliased: true, 6109 Amend: true, 6110 IsAutoRefresh: true, 6111 NoReRefresh: true, 6112 RequireTypeBase: true, 6113 } 6114 flags = flags.ForSnapSetup() 6115 6116 // certain flags get reset, others are not touched 6117 c.Check(flags, DeepEquals, snapstate.Flags{ 6118 DevMode: true, 6119 JailMode: true, 6120 Classic: true, 6121 TryMode: true, 6122 Revert: true, 6123 RemoveSnapPath: true, 6124 IgnoreValidation: true, 6125 Required: true, 6126 SkipConfigure: false, 6127 Unaliased: true, 6128 Amend: true, 6129 IsAutoRefresh: true, 6130 NoReRefresh: false, 6131 RequireTypeBase: false, 6132 }) 6133 } 6134 6135 const servicesSnap = `name: hello-snap 6136 version: 1 6137 apps: 6138 hello: 6139 command: bin/hello 6140 svc1: 6141 command: bin/hello 6142 daemon: forking 6143 before: [svc2] 6144 svc2: 6145 command: bin/hello 6146 daemon: forking 6147 after: [svc1] 6148 ` 6149 6150 func (s *snapmgrTestSuite) runStartSnapServicesWithDisabledServices(c *C, disabled ...string) { 6151 s.state.Lock() 6152 defer s.state.Unlock() 6153 6154 si := &snap.SideInfo{RealName: "hello-snap", SnapID: "hello-snap-id", Revision: snap.R(1)} 6155 snaptest.MockSnap(c, servicesSnap, si) 6156 6157 snapstate.Set(s.state, "hello-snap", &snapstate.SnapState{ 6158 Active: true, 6159 Sequence: []*snap.SideInfo{si}, 6160 Current: si.Revision, 6161 SnapType: "app", 6162 LastActiveDisabledServices: disabled, 6163 }) 6164 6165 // using MockSnap, we want to read the bits on disk 6166 snapstate.MockSnapReadInfo(snap.ReadInfo) 6167 6168 chg := s.state.NewChange("services..", "") 6169 t := s.state.NewTask("start-snap-services", "") 6170 sup := &snapstate.SnapSetup{SideInfo: si} 6171 t.Set("snap-setup", sup) 6172 chg.AddTask(t) 6173 6174 s.state.Unlock() 6175 defer s.se.Stop() 6176 s.settle(c) 6177 s.state.Lock() 6178 6179 c.Check(chg.Status(), Equals, state.DoneStatus) 6180 6181 expected := fakeOps{ 6182 { 6183 op: "start-snap-services", 6184 path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"), 6185 services: []string{"svc1", "svc2"}, 6186 }, 6187 } 6188 c.Check(s.fakeBackend.ops, DeepEquals, expected) 6189 } 6190 6191 func (s *snapmgrTestSuite) TestStartSnapServicesWithDisabledServicesNowApp(c *C) { 6192 // mock the logger 6193 buf, loggerRestore := logger.MockLogger() 6194 defer loggerRestore() 6195 6196 s.runStartSnapServicesWithDisabledServices(c, "hello") 6197 6198 // check the log for the notice 6199 c.Assert(buf.String(), Matches, `.*previously disabled service hello is now an app and not a service\n.*`) 6200 } 6201 6202 func (s *snapmgrTestSuite) TestStartSnapServicesWithDisabledServicesMissing(c *C) { 6203 // mock the logger 6204 buf, loggerRestore := logger.MockLogger() 6205 defer loggerRestore() 6206 6207 s.runStartSnapServicesWithDisabledServices(c, "old-disabled-svc") 6208 6209 // check the log for the notice 6210 c.Assert(buf.String(), Matches, `.*previously disabled service old-disabled-svc no longer exists\n.*`) 6211 } 6212 6213 func (s *snapmgrTestSuite) TestStartSnapServicesUndo(c *C) { 6214 s.state.Lock() 6215 defer s.state.Unlock() 6216 6217 si := &snap.SideInfo{RealName: "hello-snap", SnapID: "hello-snap-id", Revision: snap.R(1)} 6218 snaptest.MockSnap(c, servicesSnap, si) 6219 6220 snapstate.Set(s.state, "hello-snap", &snapstate.SnapState{ 6221 Active: true, 6222 Sequence: []*snap.SideInfo{si}, 6223 Current: si.Revision, 6224 SnapType: "app", 6225 LastActiveDisabledServices: []string{"old-svc"}, 6226 }) 6227 6228 // using MockSnap, we want to read the bits on disk 6229 snapstate.MockSnapReadInfo(snap.ReadInfo) 6230 6231 chg := s.state.NewChange("services..", "") 6232 t := s.state.NewTask("start-snap-services", "") 6233 sup := &snapstate.SnapSetup{SideInfo: si} 6234 t.Set("snap-setup", sup) 6235 chg.AddTask(t) 6236 terr := s.state.NewTask("error-trigger", "provoking total undo") 6237 terr.WaitFor(t) 6238 terr.JoinLane(t.Lanes()[0]) 6239 chg.AddTask(terr) 6240 6241 s.state.Unlock() 6242 defer s.se.Stop() 6243 s.settle(c) 6244 s.state.Lock() 6245 6246 c.Check(chg.Status(), Equals, state.ErrorStatus) 6247 c.Check(t.Status(), Equals, state.UndoneStatus) 6248 6249 expected := fakeOps{ 6250 { 6251 op: "start-snap-services", 6252 path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"), 6253 services: []string{"svc1", "svc2"}, 6254 }, 6255 { 6256 op: "stop-snap-services:", 6257 path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"), 6258 }, 6259 } 6260 c.Check(s.fakeBackend.ops, DeepEquals, expected) 6261 6262 var oldDisabledSvcs []string 6263 c.Check(t.Get("old-last-active-disabled-services", &oldDisabledSvcs), IsNil) 6264 c.Check(oldDisabledSvcs, DeepEquals, []string{"old-svc"}) 6265 } 6266 6267 func (s *snapmgrTestSuite) TestStopSnapServicesUndo(c *C) { 6268 s.state.Lock() 6269 defer s.state.Unlock() 6270 6271 prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled 6272 s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1"} 6273 6274 // reset the services to what they were before after the test is done 6275 defer func() { 6276 s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled 6277 }() 6278 6279 si := &snap.SideInfo{RealName: "hello-snap", SnapID: "hello-snap-id", Revision: snap.R(1)} 6280 snaptest.MockSnap(c, servicesSnap, si) 6281 6282 snapstate.Set(s.state, "hello-snap", &snapstate.SnapState{ 6283 Active: true, 6284 Sequence: []*snap.SideInfo{si}, 6285 Current: si.Revision, 6286 SnapType: "app", 6287 LastActiveDisabledServices: []string{"old-svc"}, 6288 }) 6289 6290 // using MockSnap, we want to read the bits on disk 6291 snapstate.MockSnapReadInfo(snap.ReadInfo) 6292 6293 chg := s.state.NewChange("services..", "") 6294 t := s.state.NewTask("stop-snap-services", "") 6295 sup := &snapstate.SnapSetup{SideInfo: si} 6296 t.Set("snap-setup", sup) 6297 chg.AddTask(t) 6298 terr := s.state.NewTask("error-trigger", "provoking total undo") 6299 terr.WaitFor(t) 6300 terr.JoinLane(t.Lanes()[0]) 6301 chg.AddTask(terr) 6302 6303 s.state.Unlock() 6304 defer s.se.Stop() 6305 s.settle(c) 6306 s.state.Lock() 6307 6308 c.Check(chg.Status(), Equals, state.ErrorStatus) 6309 c.Check(t.Status(), Equals, state.UndoneStatus) 6310 6311 expected := fakeOps{ 6312 { 6313 op: "stop-snap-services:", 6314 path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"), 6315 }, 6316 { 6317 op: "current-snap-service-states", 6318 disabledServices: []string{"svc1"}, 6319 }, 6320 { 6321 op: "start-snap-services", 6322 services: []string{"svc1", "svc2"}, 6323 disabledServices: []string{"svc1"}, 6324 path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"), 6325 }, 6326 } 6327 c.Check(s.fakeBackend.ops, DeepEquals, expected) 6328 6329 var oldDisabledSvcs []string 6330 c.Check(t.Get("old-last-active-disabled-services", &oldDisabledSvcs), IsNil) 6331 c.Check(oldDisabledSvcs, DeepEquals, []string{"old-svc"}) 6332 6333 var disabled []string 6334 c.Check(t.Get("disabled-services", &disabled), IsNil) 6335 c.Check(disabled, DeepEquals, []string{"svc1"}) 6336 } 6337 6338 func (s *snapmgrTestSuite) TestStopSnapServicesErrInUndo(c *C) { 6339 s.state.Lock() 6340 defer s.state.Unlock() 6341 6342 si := &snap.SideInfo{RealName: "hello-snap", SnapID: "hello-snap-id", Revision: snap.R(1)} 6343 snaptest.MockSnap(c, servicesSnap, si) 6344 6345 snapstate.Set(s.state, "hello-snap", &snapstate.SnapState{ 6346 Active: true, 6347 Sequence: []*snap.SideInfo{si}, 6348 Current: si.Revision, 6349 SnapType: "app", 6350 }) 6351 6352 // using MockSnap, we want to read the bits on disk 6353 snapstate.MockSnapReadInfo(snap.ReadInfo) 6354 6355 chg := s.state.NewChange("services..", "") 6356 t := s.state.NewTask("stop-snap-services", "") 6357 sup := &snapstate.SnapSetup{SideInfo: si} 6358 t.Set("snap-setup", sup) 6359 chg.AddTask(t) 6360 terr := s.state.NewTask("error-trigger", "provoking total undo") 6361 terr.WaitFor(t) 6362 terr.JoinLane(t.Lanes()[0]) 6363 chg.AddTask(terr) 6364 6365 s.fakeBackend.maybeInjectErr = func(op *fakeOp) error { 6366 if op.op == "start-snap-services" { 6367 return fmt.Errorf("start-snap-services mock error") 6368 } 6369 return nil 6370 } 6371 6372 s.state.Unlock() 6373 defer s.se.Stop() 6374 s.settle(c) 6375 s.state.Lock() 6376 6377 c.Assert(chg.IsReady(), Equals, true) 6378 c.Assert(chg.Err(), ErrorMatches, `(?s)cannot perform the following tasks:.*- +\(start-snap-services mock error\).*`) 6379 c.Check(chg.Status(), Equals, state.ErrorStatus) 6380 c.Check(t.Status(), Equals, state.ErrorStatus) 6381 6382 expected := fakeOps{ 6383 { 6384 op: "stop-snap-services:", 6385 path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"), 6386 }, 6387 { 6388 op: "current-snap-service-states", 6389 }, 6390 { 6391 // failed after this op 6392 op: "start-snap-services", 6393 services: []string{"svc1", "svc2"}, 6394 path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"), 6395 }, 6396 } 6397 c.Check(s.fakeBackend.ops, DeepEquals, expected) 6398 } 6399 6400 func (s *snapmgrTestSuite) TestEnsureAutoRefreshesAreDelayed(c *C) { 6401 s.state.Lock() 6402 defer s.state.Unlock() 6403 6404 t0 := time.Now() 6405 // with no changes in flight still works and we set the auto-refresh time as 6406 // at least one minute past the start of the test 6407 chgs, err := s.snapmgr.EnsureAutoRefreshesAreDelayed(time.Minute) 6408 c.Assert(err, IsNil) 6409 c.Assert(chgs, HasLen, 0) 6410 6411 var holdTime time.Time 6412 tr := config.NewTransaction(s.state) 6413 err = tr.Get("core", "refresh.hold", &holdTime) 6414 c.Assert(err, IsNil) 6415 // use After() == false in case holdTime is _exactly_ one minute later than 6416 // t0, in which case both After() and Before() will be false 6417 c.Assert(t0.Add(time.Minute).After(holdTime), Equals, false) 6418 6419 // now make some auto-refresh changes to make sure we get those figured out 6420 chg0 := s.state.NewChange("auto-refresh", "auto-refresh-the-things") 6421 chg0.AddTask(s.state.NewTask("nop", "do nothing")) 6422 6423 // make it in doing state 6424 chg0.SetStatus(state.DoingStatus) 6425 6426 // this one will be picked up too 6427 chg1 := s.state.NewChange("auto-refresh", "auto-refresh-the-things") 6428 chg1.AddTask(s.state.NewTask("nop", "do nothing")) 6429 chg1.SetStatus(state.DoStatus) 6430 6431 // this one won't, it's Done 6432 chg2 := s.state.NewChange("auto-refresh", "auto-refresh-the-things") 6433 chg2.AddTask(s.state.NewTask("nop", "do nothing")) 6434 chg2.SetStatus(state.DoneStatus) 6435 6436 // nor this one, it's Undone 6437 chg3 := s.state.NewChange("auto-refresh", "auto-refresh-the-things") 6438 chg3.AddTask(s.state.NewTask("nop", "do nothing")) 6439 chg3.SetStatus(state.UndoneStatus) 6440 6441 // now we get our change ID returned when calling EnsureAutoRefreshesAreDelayed 6442 chgs, err = s.snapmgr.EnsureAutoRefreshesAreDelayed(time.Minute) 6443 c.Assert(err, IsNil) 6444 // more helpful error message if we first compare the change ID's 6445 expids := []string{chg0.ID(), chg1.ID()} 6446 sort.Strings(expids) 6447 c.Assert(chgs, HasLen, len(expids)) 6448 gotids := []string{chgs[0].ID(), chgs[1].ID()} 6449 sort.Strings(gotids) 6450 c.Assert(expids, DeepEquals, gotids) 6451 6452 sort.SliceStable(chgs, func(i, j int) bool { 6453 return chgs[i].ID() < chgs[j].ID() 6454 }) 6455 6456 c.Assert(chgs, DeepEquals, []*state.Change{chg0, chg1}) 6457 } 6458 6459 func (s *snapmgrTestSuite) TestInstallModeDisableFreshInstall(c *C) { 6460 s.state.Lock() 6461 defer s.state.Unlock() 6462 6463 oldServicesSnapYaml := servicesSnapYaml 6464 servicesSnapYaml += ` 6465 svcInstallModeDisable: 6466 daemon: simple 6467 install-mode: disable 6468 ` 6469 defer func() { servicesSnapYaml = oldServicesSnapYaml }() 6470 6471 installChg := s.state.NewChange("install", "...") 6472 installTs, err := snapstate.Install(context.Background(), s.state, "services-snap", nil, 0, snapstate.Flags{}) 6473 c.Assert(err, IsNil) 6474 installChg.AddAll(installTs) 6475 6476 s.state.Unlock() 6477 defer s.se.Stop() 6478 s.settle(c) 6479 s.state.Lock() 6480 6481 c.Assert(installChg.Err(), IsNil) 6482 c.Assert(installChg.IsReady(), Equals, true) 6483 6484 op := s.fakeBackend.ops.First("start-snap-services") 6485 c.Assert(op, Not(IsNil)) 6486 c.Check(op.disabledServices, DeepEquals, []string{"svcInstallModeDisable"}) 6487 } 6488 6489 func (s *snapmgrTestSuite) TestInstallModeDisableUpdateServiceNotDisabled(c *C) { 6490 s.state.Lock() 6491 defer s.state.Unlock() 6492 6493 oldServicesSnapYaml := servicesSnapYaml 6494 servicesSnapYaml += ` 6495 svcInstallModeDisable: 6496 daemon: simple 6497 install-mode: disable 6498 ` 6499 defer func() { servicesSnapYaml = oldServicesSnapYaml }() 6500 6501 // pretent services-snap is installed and no service is disabled in 6502 // this install (i.e. svcInstallModeDisable is active) 6503 si := &snap.SideInfo{ 6504 RealName: "services-snap", SnapID: "services-snap-id", Revision: snap.R(7), 6505 } 6506 snapstate.Set(s.state, "services-snap", &snapstate.SnapState{ 6507 Sequence: []*snap.SideInfo{si}, 6508 Current: si.Revision, 6509 Active: true, 6510 }) 6511 snaptest.MockSnap(c, string(servicesSnapYaml), si) 6512 6513 updateChg := s.state.NewChange("refresh", "...") 6514 updateTs, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}) 6515 c.Assert(err, IsNil) 6516 updateChg.AddAll(updateTs) 6517 6518 s.state.Unlock() 6519 defer s.se.Stop() 6520 s.settle(c) 6521 s.state.Lock() 6522 6523 c.Assert(updateChg.Err(), IsNil) 6524 c.Assert(updateChg.IsReady(), Equals, true) 6525 6526 op := s.fakeBackend.ops.First("start-snap-services") 6527 c.Assert(op, Not(IsNil)) 6528 c.Check(op.disabledServices, HasLen, 0) 6529 } 6530 6531 func (s *snapmgrTestSuite) TestInstallModeDisableFreshInstallEnabledByHook(c *C) { 6532 st := s.state 6533 st.Lock() 6534 defer st.Unlock() 6535 6536 oldServicesSnapYaml := servicesSnapYaml 6537 servicesSnapYaml += ` 6538 svcInstallModeDisable: 6539 daemon: simple 6540 install-mode: disable 6541 ` 6542 defer func() { servicesSnapYaml = oldServicesSnapYaml }() 6543 6544 // XXX: should this become part of managers_test.go ? 6545 // pretent we have a hook that enables the service on install 6546 runner := s.o.TaskRunner() 6547 runner.AddHandler("run-hook", func(t *state.Task, _ *tomb.Tomb) error { 6548 var snapst snapstate.SnapState 6549 st.Lock() 6550 err := snapstate.Get(st, "services-snap", &snapst) 6551 st.Unlock() 6552 c.Assert(err, IsNil) 6553 snapst.ServicesEnabledByHooks = []string{"svcInstallModeDisable"} 6554 st.Lock() 6555 snapstate.Set(st, "services-snap", &snapst) 6556 st.Unlock() 6557 return nil 6558 }, nil) 6559 6560 installChg := s.state.NewChange("install", "...") 6561 installTs, err := snapstate.Install(context.Background(), s.state, "services-snap", nil, 0, snapstate.Flags{}) 6562 c.Assert(err, IsNil) 6563 installChg.AddAll(installTs) 6564 6565 s.state.Unlock() 6566 defer s.se.Stop() 6567 s.settle(c) 6568 s.state.Lock() 6569 6570 c.Assert(installChg.Err(), IsNil) 6571 c.Assert(installChg.IsReady(), Equals, true) 6572 6573 op := s.fakeBackend.ops.First("start-snap-services") 6574 c.Assert(op, Not(IsNil)) 6575 c.Check(op.disabledServices, HasLen, 0) 6576 } 6577 6578 func (s *snapmgrTestSuite) TestSnapdRefreshTasks(c *C) { 6579 s.state.Lock() 6580 defer s.state.Unlock() 6581 6582 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 6583 Active: true, 6584 Sequence: []*snap.SideInfo{ 6585 {RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)}, 6586 }, 6587 Current: snap.R(1), 6588 SnapType: "snapd", 6589 }) 6590 6591 chg := s.state.NewChange("snapd-refresh", "refresh snapd") 6592 ts, err := snapstate.Update(s.state, "snapd", nil, 0, snapstate.Flags{}) 6593 c.Assert(err, IsNil) 6594 c.Assert(err, IsNil) 6595 chg.AddAll(ts) 6596 6597 s.state.Unlock() 6598 defer s.se.Stop() 6599 s.settle(c) 6600 s.state.Lock() 6601 6602 // various backend operations, but no unlink-current-snap 6603 expected := fakeOps{ 6604 { 6605 op: "storesvc-snap-action", 6606 curSnaps: []store.CurrentSnap{ 6607 { 6608 InstanceName: "snapd", 6609 SnapID: "snapd-snap-id", 6610 Revision: snap.R(1), 6611 RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1), 6612 Epoch: snap.E("1*"), 6613 }, 6614 }, 6615 }, 6616 { 6617 op: "storesvc-snap-action:action", 6618 action: store.SnapAction{ 6619 Action: "refresh", 6620 SnapID: "snapd-snap-id", 6621 InstanceName: "snapd", 6622 Flags: store.SnapActionEnforceValidation, 6623 }, 6624 revno: snap.R(11), 6625 }, 6626 { 6627 op: "storesvc-download", 6628 name: "snapd", 6629 }, 6630 { 6631 op: "validate-snap:Doing", 6632 name: "snapd", 6633 revno: snap.R(11), 6634 }, 6635 { 6636 op: "current", 6637 old: filepath.Join(dirs.SnapMountDir, "snapd/1"), 6638 }, 6639 { 6640 op: "open-snap-file", 6641 path: filepath.Join(dirs.SnapBlobDir, "snapd_11.snap"), 6642 sinfo: snap.SideInfo{ 6643 RealName: "snapd", 6644 SnapID: "snapd-snap-id", 6645 Revision: snap.R(11), 6646 }, 6647 }, 6648 { 6649 op: "setup-snap", 6650 name: "snapd", 6651 path: filepath.Join(dirs.SnapBlobDir, "snapd_11.snap"), 6652 revno: snap.R(11), 6653 }, 6654 { 6655 op: "remove-snap-aliases", 6656 name: "snapd", 6657 }, 6658 { 6659 op: "copy-data", 6660 path: filepath.Join(dirs.SnapMountDir, "snapd/11"), 6661 old: filepath.Join(dirs.SnapMountDir, "snapd/1"), 6662 }, 6663 { 6664 op: "setup-profiles:Doing", 6665 name: "snapd", 6666 revno: snap.R(11), 6667 }, 6668 { 6669 op: "candidate", 6670 sinfo: snap.SideInfo{ 6671 RealName: "snapd", 6672 SnapID: "snapd-snap-id", 6673 Revision: snap.R(11), 6674 }, 6675 }, 6676 { 6677 op: "link-snap", 6678 path: filepath.Join(dirs.SnapMountDir, "snapd/11"), 6679 }, 6680 { 6681 op: "auto-connect:Doing", 6682 name: "snapd", 6683 revno: snap.R(11), 6684 }, 6685 { 6686 op: "update-aliases", 6687 }, 6688 { 6689 op: "cleanup-trash", 6690 name: "snapd", 6691 revno: snap.R(11), 6692 }, 6693 } 6694 // start with an easier-to-read error if this fails: 6695 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 6696 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 6697 6698 // verify that the R(2) version is active now and R(7) is still there 6699 var snapst snapstate.SnapState 6700 err = snapstate.Get(s.state, "snapd", &snapst) 6701 c.Assert(err, IsNil) 6702 6703 c.Assert(snapst.Active, Equals, true) 6704 c.Assert(snapst.Current, Equals, snap.R(11)) 6705 } 6706 6707 type installTestType struct { 6708 t snap.Type 6709 } 6710 6711 func (t *installTestType) InstanceName() string { 6712 panic("not expected") 6713 } 6714 6715 func (t *installTestType) Type() snap.Type { 6716 return t.t 6717 } 6718 6719 func (t *installTestType) SnapBase() string { 6720 panic("not expected") 6721 } 6722 6723 func (t *installTestType) DownloadSize() int64 { 6724 panic("not expected") 6725 } 6726 6727 func (t *installTestType) Prereq(st *state.State) []string { 6728 panic("not expected") 6729 } 6730 6731 func (s *snapmgrTestSuite) TestMinimalInstallInfoSortByType(c *C) { 6732 snaps := []snapstate.MinimalInstallInfo{ 6733 &installTestType{snap.TypeApp}, 6734 &installTestType{snap.TypeBase}, 6735 &installTestType{snap.TypeApp}, 6736 &installTestType{snap.TypeSnapd}, 6737 &installTestType{snap.TypeKernel}, 6738 &installTestType{snap.TypeGadget}, 6739 } 6740 6741 sort.Sort(snapstate.ByType(snaps)) 6742 c.Check(snaps, DeepEquals, []snapstate.MinimalInstallInfo{ 6743 &installTestType{snap.TypeSnapd}, 6744 &installTestType{snap.TypeKernel}, 6745 &installTestType{snap.TypeBase}, 6746 &installTestType{snap.TypeGadget}, 6747 &installTestType{snap.TypeApp}, 6748 &installTestType{snap.TypeApp}}) 6749 }