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