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