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