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