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