github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/snapstate/snapstate_install_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2020 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 "fmt" 25 "io/ioutil" 26 "path/filepath" 27 "time" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/boot" 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/gadget" 34 "github.com/snapcore/snapd/interfaces" 35 "github.com/snapcore/snapd/osutil" 36 "github.com/snapcore/snapd/overlord/auth" 37 "github.com/snapcore/snapd/overlord/configstate/config" 38 "github.com/snapcore/snapd/overlord/hookstate" 39 "github.com/snapcore/snapd/overlord/ifacestate/ifacerepo" 40 "github.com/snapcore/snapd/overlord/snapstate" 41 "github.com/snapcore/snapd/overlord/snapstate/backend" 42 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 43 "github.com/snapcore/snapd/overlord/state" 44 "github.com/snapcore/snapd/release" 45 "github.com/snapcore/snapd/snap" 46 "github.com/snapcore/snapd/snap/snaptest" 47 "github.com/snapcore/snapd/store" 48 "github.com/snapcore/snapd/testutil" 49 50 // So it registers Configure. 51 _ "github.com/snapcore/snapd/overlord/configstate" 52 ) 53 54 func verifyInstallTasks(c *C, opts, discards int, ts *state.TaskSet, st *state.State) { 55 kinds := taskKinds(ts.Tasks()) 56 57 expected := []string{ 58 "prerequisites", 59 "download-snap", 60 "validate-snap", 61 "mount-snap", 62 } 63 if opts&unlinkBefore != 0 { 64 expected = append(expected, 65 "stop-snap-services", 66 "remove-aliases", 67 "unlink-current-snap", 68 ) 69 } 70 if opts&updatesGadget != 0 { 71 expected = append(expected, "update-gadget-assets") 72 } 73 expected = append(expected, 74 "copy-snap-data", 75 "setup-profiles", 76 "link-snap", 77 ) 78 expected = append(expected, 79 "auto-connect", 80 "set-auto-aliases", 81 "setup-aliases", 82 "run-hook[install]", 83 "start-snap-services") 84 for i := 0; i < discards; i++ { 85 expected = append(expected, 86 "clear-snap", 87 "discard-snap", 88 ) 89 } 90 if opts&cleanupAfter != 0 { 91 expected = append(expected, 92 "cleanup", 93 ) 94 } 95 if opts&noConfigure == 0 { 96 expected = append(expected, 97 "run-hook[configure]", 98 ) 99 } 100 expected = append(expected, 101 "run-hook[check-health]", 102 ) 103 104 c.Assert(kinds, DeepEquals, expected) 105 } 106 107 func (s *snapmgrTestSuite) TestInstallDevModeConfinementFiltering(c *C) { 108 s.state.Lock() 109 defer s.state.Unlock() 110 111 // if a snap is devmode, you can't install it without --devmode 112 opts := &snapstate.RevisionOptions{Channel: "channel-for-devmode"} 113 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 114 c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`) 115 116 // if a snap is devmode, you *can* install it with --devmode 117 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{DevMode: true}) 118 c.Assert(err, IsNil) 119 120 // if a snap is *not* devmode, you can still install it with --devmode 121 opts.Channel = "channel-for-strict" 122 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{DevMode: true}) 123 c.Assert(err, IsNil) 124 } 125 126 func (s *snapmgrTestSuite) TestInstallClassicConfinementFiltering(c *C) { 127 restore := maybeMockClassicSupport(c) 128 defer restore() 129 130 s.state.Lock() 131 defer s.state.Unlock() 132 133 // if a snap is classic, you can't install it without --classic 134 opts := &snapstate.RevisionOptions{Channel: "channel-for-classic"} 135 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 136 c.Assert(err, ErrorMatches, `.* requires classic confinement`) 137 138 // if a snap is classic, you *can* install it with --classic 139 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true}) 140 c.Assert(err, IsNil) 141 142 // if a snap is *not* classic, but can install it with --classic which gets ignored 143 opts.Channel = "channel-for-strict" 144 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true}) 145 c.Assert(err, IsNil) 146 } 147 148 func (s *snapmgrTestSuite) TestInstallTasks(c *C) { 149 s.state.Lock() 150 defer s.state.Unlock() 151 152 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 153 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}) 154 c.Assert(err, IsNil) 155 156 verifyInstallTasks(c, 0, 0, ts, s.state) 157 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 158 } 159 160 func (s *snapmgrTestSuite) TestInstallTaskEdgesForPreseeding(c *C) { 161 s.state.Lock() 162 defer s.state.Unlock() 163 164 mockSnap := makeTestSnap(c, `name: some-snap 165 version: 1.0 166 `) 167 168 for _, skipConfig := range []bool{false, true} { 169 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{SkipConfigure: skipConfig}) 170 c.Assert(err, IsNil) 171 172 te, err := ts.Edge(snapstate.BeginEdge) 173 c.Assert(err, IsNil) 174 c.Check(te.Kind(), Equals, "prerequisites") 175 176 te, err = ts.Edge(snapstate.BeforeHooksEdge) 177 c.Assert(err, IsNil) 178 c.Check(te.Kind(), Equals, "setup-aliases") 179 180 te, err = ts.Edge(snapstate.HooksEdge) 181 c.Assert(err, IsNil) 182 c.Assert(te.Kind(), Equals, "run-hook") 183 184 var hsup *hookstate.HookSetup 185 c.Assert(te.Get("hook-setup", &hsup), IsNil) 186 c.Check(hsup.Hook, Equals, "install") 187 c.Check(hsup.Snap, Equals, "some-snap") 188 } 189 } 190 191 func (s *snapmgrTestSuite) TestInstallSnapdSnapType(c *C) { 192 s.state.Lock() 193 defer s.state.Unlock() 194 195 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 196 ts, err := snapstate.Install(context.Background(), s.state, "snapd", opts, 0, snapstate.Flags{}) 197 c.Assert(err, IsNil) 198 199 verifyInstallTasks(c, noConfigure, 0, ts, s.state) 200 201 snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0]) 202 c.Assert(err, IsNil) 203 c.Check(snapsup.Type, Equals, snap.TypeSnapd) 204 } 205 206 func (s *snapmgrTestSuite) TestInstallCohortTasks(c *C) { 207 s.state.Lock() 208 defer s.state.Unlock() 209 210 opts := &snapstate.RevisionOptions{Channel: "some-channel", CohortKey: "what"} 211 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}) 212 c.Assert(err, IsNil) 213 snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0]) 214 c.Assert(err, IsNil) 215 c.Check(snapsup.CohortKey, Equals, "what") 216 217 verifyInstallTasks(c, 0, 0, ts, s.state) 218 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 219 } 220 221 func (s *snapmgrTestSuite) TestInstallWithDeviceContext(c *C) { 222 s.state.Lock() 223 defer s.state.Unlock() 224 225 // unset the global store, it will need to come via the device context 226 snapstate.ReplaceStore(s.state, nil) 227 228 deviceCtx := &snapstatetest.TrivialDeviceContext{CtxStore: s.fakeStore} 229 230 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 231 ts, err := snapstate.InstallWithDeviceContext(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}, deviceCtx, "") 232 c.Assert(err, IsNil) 233 234 verifyInstallTasks(c, 0, 0, ts, s.state) 235 c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())) 236 } 237 238 func (s *snapmgrTestSuite) TestInstallHookNotRunForInstalledSnap(c *C) { 239 s.state.Lock() 240 defer s.state.Unlock() 241 242 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 243 Active: true, 244 Sequence: []*snap.SideInfo{ 245 {RealName: "some-snap", Revision: snap.R(7)}, 246 }, 247 Current: snap.R(7), 248 SnapType: "app", 249 }) 250 251 mockSnap := makeTestSnap(c, `name: some-snap 252 version: 1.0 253 epoch: 1* 254 `) 255 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{}) 256 c.Assert(err, IsNil) 257 258 runHooks := tasksWithKind(ts, "run-hook") 259 // no install hook task 260 c.Assert(taskKinds(runHooks), DeepEquals, []string{ 261 "run-hook[pre-refresh]", 262 "run-hook[post-refresh]", 263 "run-hook[configure]", 264 "run-hook[check-health]", 265 }) 266 } 267 268 func (s *snapmgrTestSuite) TestInstallFailsOnDisabledSnap(c *C) { 269 s.state.Lock() 270 defer s.state.Unlock() 271 272 snapst := &snapstate.SnapState{ 273 Active: false, 274 TrackingChannel: "channel/stable", 275 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}}, 276 Current: snap.R(2), 277 SnapType: "app", 278 } 279 snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}} 280 _, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "", nil) 281 c.Assert(err, NotNil) 282 c.Assert(err, ErrorMatches, `cannot update disabled snap "some-snap"`) 283 } 284 285 func dummyInUseCheck(snap.Type) (boot.InUseFunc, error) { 286 return func(string, snap.Revision) bool { 287 return false 288 }, nil 289 } 290 291 func (s *snapmgrTestSuite) TestInstallFailsOnBusySnap(c *C) { 292 s.state.Lock() 293 defer s.state.Unlock() 294 295 // With the refresh-app-awareness feature enabled. 296 tr := config.NewTransaction(s.state) 297 tr.Set("core", "experimental.refresh-app-awareness", true) 298 tr.Commit() 299 300 // With a snap state indicating a snap is already installed. 301 snapst := &snapstate.SnapState{ 302 Active: true, 303 Sequence: []*snap.SideInfo{ 304 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 305 }, 306 Current: snap.R(1), 307 SnapType: "app", 308 } 309 snapstate.Set(s.state, "some-snap", snapst) 310 311 // With a snap info indicating it has an application called "app" 312 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 313 if name != "some-snap" { 314 return s.fakeBackend.ReadInfo(name, si) 315 } 316 info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp} 317 info.Apps = map[string]*snap.AppInfo{ 318 "app": {Snap: info, Name: "app"}, 319 } 320 return info, nil 321 }) 322 323 // mock that "some-snap" has an app and that this app has pids running 324 restore := snapstate.MockPidsOfSnap(func(instanceName string) (map[string][]int, error) { 325 c.Assert(instanceName, Equals, "some-snap") 326 return map[string][]int{ 327 "snap.some-snap.app": {1234}, 328 }, nil 329 }) 330 defer restore() 331 332 // Attempt to install revision 2 of the snap. 333 snapsup := &snapstate.SnapSetup{ 334 SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 335 } 336 337 // And observe that we cannot refresh because the snap is busy. 338 _, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "", dummyInUseCheck) 339 c.Assert(err, ErrorMatches, `snap "some-snap" has running apps \(app\)`) 340 341 // The state records the time of the failed refresh operation. 342 err = snapstate.Get(s.state, "some-snap", snapst) 343 c.Assert(err, IsNil) 344 c.Check(snapst.RefreshInhibitedTime, NotNil) 345 } 346 347 func (s *snapmgrTestSuite) TestInstallDespiteBusySnap(c *C) { 348 s.state.Lock() 349 defer s.state.Unlock() 350 351 // With the refresh-app-awareness feature enabled. 352 tr := config.NewTransaction(s.state) 353 tr.Set("core", "experimental.refresh-app-awareness", true) 354 tr.Commit() 355 356 // With a snap state indicating a snap is already installed and it failed 357 // to refresh over a week ago. Use UTC and Round to have predictable 358 // behaviour across time-zones and with enough precision loss to be 359 // compatible with the serialization format. 360 var longAgo = time.Now().UTC().Round(time.Second).Add(-time.Hour * 24 * 8) 361 snapst := &snapstate.SnapState{ 362 Active: true, 363 Sequence: []*snap.SideInfo{ 364 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 365 }, 366 Current: snap.R(1), 367 SnapType: "app", 368 RefreshInhibitedTime: &longAgo, 369 } 370 snapstate.Set(s.state, "some-snap", snapst) 371 372 // With a snap info indicating it has an application called "app" 373 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 374 if name != "some-snap" { 375 return s.fakeBackend.ReadInfo(name, si) 376 } 377 info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp} 378 info.Apps = map[string]*snap.AppInfo{ 379 "app": {Snap: info, Name: "app"}, 380 } 381 return info, nil 382 }) 383 // And with cgroup information indicating the app has a process with pid 1234. 384 restore := snapstate.MockPidsOfSnap(func(instanceName string) (map[string][]int, error) { 385 c.Assert(instanceName, Equals, "some-snap") 386 return map[string][]int{ 387 "snap.some-snap.some-app": {1234}, 388 }, nil 389 }) 390 defer restore() 391 392 // Attempt to install revision 2 of the snap. 393 snapsup := &snapstate.SnapSetup{ 394 SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, 395 } 396 397 // And observe that refresh occurred regardless of the running process. 398 _, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "", dummyInUseCheck) 399 c.Assert(err, IsNil) 400 } 401 402 func (s *snapmgrTestSuite) TestInstallFailsOnSystem(c *C) { 403 s.state.Lock() 404 defer s.state.Unlock() 405 406 snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "system", SnapID: "some-snap-id", Revision: snap.R(1)}} 407 _, err := snapstate.DoInstall(s.state, nil, snapsup, 0, "", nil) 408 c.Assert(err, NotNil) 409 c.Assert(err, ErrorMatches, `cannot install reserved snap name 'system'`) 410 } 411 412 func (s *snapmgrTestSuite) TestDoInstallChannelDefault(c *C) { 413 s.state.Lock() 414 defer s.state.Unlock() 415 416 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 417 c.Assert(err, IsNil) 418 419 var snapsup snapstate.SnapSetup 420 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 421 c.Assert(err, IsNil) 422 423 c.Check(snapsup.Channel, Equals, "stable") 424 } 425 426 func (s *snapmgrTestSuite) TestInstallRevision(c *C) { 427 s.state.Lock() 428 defer s.state.Unlock() 429 430 opts := &snapstate.RevisionOptions{Revision: snap.R(7)} 431 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}) 432 c.Assert(err, IsNil) 433 434 var snapsup snapstate.SnapSetup 435 err = ts.Tasks()[0].Get("snap-setup", &snapsup) 436 c.Assert(err, IsNil) 437 438 c.Check(snapsup.Revision(), Equals, snap.R(7)) 439 } 440 441 func (s *snapmgrTestSuite) TestInstallTooEarly(c *C) { 442 s.state.Lock() 443 defer s.state.Unlock() 444 445 s.state.Set("seeded", nil) 446 447 _, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 448 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 449 c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`) 450 } 451 452 func (s *snapmgrTestSuite) TestInstallConflict(c *C) { 453 s.state.Lock() 454 defer s.state.Unlock() 455 456 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 457 c.Assert(err, IsNil) 458 // need a change to make the tasks visible 459 s.state.NewChange("install", "...").AddAll(ts) 460 461 _, err = snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 462 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 463 c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`) 464 } 465 466 func (s *snapmgrTestSuite) TestInstallAliasConflict(c *C) { 467 s.state.Lock() 468 defer s.state.Unlock() 469 470 snapstate.Set(s.state, "otherfoosnap", &snapstate.SnapState{ 471 Sequence: []*snap.SideInfo{ 472 {RealName: "otherfoosnap", Revision: snap.R(30)}, 473 }, 474 Current: snap.R(30), 475 Active: true, 476 Aliases: map[string]*snapstate.AliasTarget{ 477 "foo.bar": {Manual: "bar"}, 478 }, 479 SnapType: "app", 480 }) 481 482 _, err := snapstate.Install(context.Background(), s.state, "foo", nil, 0, snapstate.Flags{}) 483 c.Assert(err, ErrorMatches, `snap "foo" command namespace conflicts with alias "foo\.bar" for "otherfoosnap" snap`) 484 } 485 486 func (s *snapmgrTestSuite) TestInstallStrictIgnoresClassic(c *C) { 487 restore := maybeMockClassicSupport(c) 488 defer restore() 489 490 s.state.Lock() 491 defer s.state.Unlock() 492 493 opts := &snapstate.RevisionOptions{Channel: "channel-for-strict"} 494 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true}) 495 c.Assert(err, IsNil) 496 497 c.Assert(err, IsNil) 498 499 chg := s.state.NewChange("install", "install snap") 500 chg.AddAll(ts) 501 502 s.state.Unlock() 503 defer s.se.Stop() 504 s.settle(c) 505 s.state.Lock() 506 507 c.Assert(chg.Err(), IsNil) 508 c.Assert(chg.IsReady(), Equals, true) 509 510 // verify snap is *not* classic 511 var snapst snapstate.SnapState 512 err = snapstate.Get(s.state, "some-snap", &snapst) 513 c.Assert(err, IsNil) 514 c.Check(snapst.TrackingChannel, Equals, "channel-for-strict/stable") 515 c.Check(snapst.Classic, Equals, false) 516 } 517 518 func (s *snapmgrTestSuite) TestInstallSnapWithDefaultTrack(c *C) { 519 restore := maybeMockClassicSupport(c) 520 defer restore() 521 522 s.state.Lock() 523 defer s.state.Unlock() 524 525 opts := &snapstate.RevisionOptions{Channel: "candidate"} 526 ts, err := snapstate.Install(context.Background(), s.state, "some-snap-with-default-track", opts, s.user.ID, snapstate.Flags{}) 527 c.Assert(err, IsNil) 528 529 chg := s.state.NewChange("install", "install snap") 530 chg.AddAll(ts) 531 532 s.state.Unlock() 533 defer s.se.Stop() 534 s.settle(c) 535 s.state.Lock() 536 537 c.Assert(chg.Err(), IsNil) 538 c.Assert(chg.IsReady(), Equals, true) 539 540 // verify snap is in the 2.0 track 541 var snapst snapstate.SnapState 542 err = snapstate.Get(s.state, "some-snap-with-default-track", &snapst) 543 c.Assert(err, IsNil) 544 c.Check(snapst.TrackingChannel, Equals, "2.0/candidate") 545 } 546 547 func (s *snapmgrTestSuite) TestInstallManySnapOneWithDefaultTrack(c *C) { 548 restore := maybeMockClassicSupport(c) 549 defer restore() 550 551 s.state.Lock() 552 defer s.state.Unlock() 553 554 snapNames := []string{"some-snap", "some-snap-with-default-track"} 555 installed, tss, err := snapstate.InstallMany(s.state, snapNames, s.user.ID) 556 c.Assert(err, IsNil) 557 c.Assert(installed, DeepEquals, snapNames) 558 559 chg := s.state.NewChange("install", "install two snaps") 560 for _, ts := range tss { 561 chg.AddAll(ts) 562 } 563 564 s.state.Unlock() 565 defer s.se.Stop() 566 s.settle(c) 567 s.state.Lock() 568 569 c.Assert(chg.Err(), IsNil) 570 c.Assert(chg.IsReady(), Equals, true) 571 572 // verify snap is in the 2.0 track 573 var snapst snapstate.SnapState 574 err = snapstate.Get(s.state, "some-snap-with-default-track", &snapst) 575 c.Assert(err, IsNil) 576 c.Check(snapst.TrackingChannel, Equals, "2.0/stable") 577 578 err = snapstate.Get(s.state, "some-snap", &snapst) 579 c.Assert(err, IsNil) 580 c.Check(snapst.TrackingChannel, Equals, "latest/stable") 581 } 582 583 // A sneakyStore changes the state when called 584 type sneakyStore struct { 585 *fakeStore 586 state *state.State 587 } 588 589 func (s sneakyStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) { 590 s.state.Lock() 591 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 592 Active: true, 593 TrackingChannel: "latest/edge", 594 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}}, 595 Current: snap.R(1), 596 SnapType: "app", 597 }) 598 s.state.Unlock() 599 return s.fakeStore.SnapAction(ctx, currentSnaps, actions, assertQuery, user, opts) 600 } 601 602 func (s *snapmgrTestSuite) TestInstallStateConflict(c *C) { 603 s.state.Lock() 604 defer s.state.Unlock() 605 606 snapstate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state}) 607 608 _, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 609 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 610 c.Assert(err, ErrorMatches, `snap "some-snap" has changes in progress`) 611 } 612 613 func (s *snapmgrTestSuite) TestInstallPathTooEarly(c *C) { 614 s.state.Lock() 615 defer s.state.Unlock() 616 617 r := snapstatetest.MockDeviceModel(nil) 618 defer r() 619 620 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0") 621 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{}) 622 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 623 c.Assert(err, ErrorMatches, `too early for operation, device model not yet acknowledged`) 624 625 } 626 627 func (s *snapmgrTestSuite) TestInstallPathConflict(c *C) { 628 s.state.Lock() 629 defer s.state.Unlock() 630 631 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{}) 632 c.Assert(err, IsNil) 633 // need a change to make the tasks visible 634 s.state.NewChange("install", "...").AddAll(ts) 635 636 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0") 637 _, _, err = snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{}) 638 c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`) 639 } 640 641 func (s *snapmgrTestSuite) TestInstallPathMissingName(c *C) { 642 s.state.Lock() 643 defer s.state.Unlock() 644 645 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0") 646 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{}, mockSnap, "", "", snapstate.Flags{}) 647 c.Assert(err, ErrorMatches, fmt.Sprintf(`internal error: snap name to install %q not provided`, mockSnap)) 648 } 649 650 func (s *snapmgrTestSuite) TestInstallPathSnapIDRevisionUnset(c *C) { 651 s.state.Lock() 652 defer s.state.Unlock() 653 654 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0") 655 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "snapididid"}, mockSnap, "", "", snapstate.Flags{}) 656 c.Assert(err, ErrorMatches, fmt.Sprintf(`internal error: snap id set to install %q but revision is unset`, mockSnap)) 657 } 658 659 func (s *snapmgrTestSuite) TestInstallPathValidateFlags(c *C) { 660 s.state.Lock() 661 defer s.state.Unlock() 662 663 mockSnap := makeTestSnap(c, `name: some-snap 664 version: 1.0 665 confinement: devmode 666 `) 667 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{}) 668 c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`) 669 } 670 671 func (s *snapmgrTestSuite) TestInstallPathStrictIgnoresClassic(c *C) { 672 restore := maybeMockClassicSupport(c) 673 defer restore() 674 675 s.state.Lock() 676 defer s.state.Unlock() 677 678 mockSnap := makeTestSnap(c, `name: some-snap 679 version: 1.0 680 confinement: strict 681 `) 682 683 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{Classic: true}) 684 c.Assert(err, IsNil) 685 686 c.Assert(err, IsNil) 687 688 chg := s.state.NewChange("install", "install snap") 689 chg.AddAll(ts) 690 691 s.state.Unlock() 692 defer s.se.Stop() 693 s.settle(c) 694 s.state.Lock() 695 696 c.Assert(chg.Err(), IsNil) 697 c.Assert(chg.IsReady(), Equals, true) 698 699 // verify snap is *not* classic 700 var snapst snapstate.SnapState 701 err = snapstate.Get(s.state, "some-snap", &snapst) 702 c.Assert(err, IsNil) 703 c.Check(snapst.Classic, Equals, false) 704 } 705 706 func (s *snapmgrTestSuite) TestInstallPathAsRefresh(c *C) { 707 s.state.Lock() 708 defer s.state.Unlock() 709 710 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 711 Active: true, 712 Flags: snapstate.Flags{DevMode: true}, 713 Sequence: []*snap.SideInfo{ 714 {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, 715 }, 716 Current: snap.R(1), 717 SnapType: "app", 718 TrackingChannel: "wibbly/stable", 719 }) 720 721 mockSnap := makeTestSnap(c, `name: some-snap 722 version: 1.0 723 epoch: 1 724 `) 725 726 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "edge", snapstate.Flags{}) 727 c.Assert(err, IsNil) 728 729 c.Assert(err, IsNil) 730 731 chg := s.state.NewChange("install", "install snap") 732 chg.AddAll(ts) 733 734 s.state.Unlock() 735 defer s.se.Stop() 736 s.settle(c) 737 s.state.Lock() 738 739 c.Assert(chg.Err(), IsNil) 740 c.Assert(chg.IsReady(), Equals, true) 741 742 // verify snap is *not* classic 743 var snapst snapstate.SnapState 744 err = snapstate.Get(s.state, "some-snap", &snapst) 745 c.Assert(err, IsNil) 746 c.Check(snapst.TrackingChannel, Equals, "wibbly/edge") 747 } 748 749 func (s *snapmgrTestSuite) TestParallelInstanceInstallNotAllowed(c *C) { 750 s.state.Lock() 751 defer s.state.Unlock() 752 753 snapstate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state}) 754 755 tr := config.NewTransaction(s.state) 756 tr.Set("core", "experimental.parallel-instances", true) 757 tr.Commit() 758 759 _, err := snapstate.Install(context.Background(), s.state, "core_foo", nil, 0, snapstate.Flags{}) 760 c.Check(err, ErrorMatches, `cannot install snap of type os as "core_foo"`) 761 762 _, err = snapstate.Install(context.Background(), s.state, "some-base_foo", nil, 0, snapstate.Flags{}) 763 c.Check(err, ErrorMatches, `cannot install snap of type base as "some-base_foo"`) 764 765 _, err = snapstate.Install(context.Background(), s.state, "some-gadget_foo", nil, 0, snapstate.Flags{}) 766 c.Check(err, ErrorMatches, `cannot install snap of type gadget as "some-gadget_foo"`) 767 768 _, err = snapstate.Install(context.Background(), s.state, "some-kernel_foo", nil, 0, snapstate.Flags{}) 769 c.Check(err, ErrorMatches, `cannot install snap of type kernel as "some-kernel_foo"`) 770 771 _, err = snapstate.Install(context.Background(), s.state, "some-snapd_foo", nil, 0, snapstate.Flags{}) 772 c.Check(err, ErrorMatches, `cannot install snap of type snapd as "some-snapd_foo"`) 773 } 774 775 func (s *snapmgrTestSuite) TestInstallPathFailsEarlyOnEpochMismatch(c *C) { 776 s.state.Lock() 777 defer s.state.Unlock() 778 779 // have epoch 1* installed 780 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 781 Active: true, 782 TrackingChannel: "latest/edge", 783 Sequence: []*snap.SideInfo{{RealName: "some-snap", Revision: snap.R(7)}}, 784 Current: snap.R(7), 785 }) 786 787 // try to install epoch 42 788 mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0\nepoch: 42\n") 789 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{}) 790 c.Assert(err, ErrorMatches, `cannot refresh "some-snap" to local snap with epoch 42, because it can't read the current epoch of 1\*`) 791 } 792 793 func (s *snapmgrTestSuite) TestInstallRunThrough(c *C) { 794 s.state.Lock() 795 defer s.state.Unlock() 796 797 // we start without the auxiliary store info 798 c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FileAbsent) 799 800 chg := s.state.NewChange("install", "install a snap") 801 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 802 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 803 c.Assert(err, IsNil) 804 chg.AddAll(ts) 805 806 s.state.Unlock() 807 defer s.se.Stop() 808 s.settle(c) 809 s.state.Lock() 810 811 // ensure all our tasks ran 812 c.Assert(chg.Err(), IsNil) 813 c.Assert(chg.IsReady(), Equals, true) 814 c.Check(snapstate.Installing(s.state), Equals, false) 815 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 816 macaroon: s.user.StoreMacaroon, 817 name: "some-snap", 818 target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 819 }}) 820 c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys)) 821 expected := fakeOps{ 822 { 823 op: "storesvc-snap-action", 824 userID: 1, 825 }, 826 { 827 op: "storesvc-snap-action:action", 828 action: store.SnapAction{ 829 Action: "install", 830 InstanceName: "some-snap", 831 Channel: "some-channel", 832 }, 833 revno: snap.R(11), 834 userID: 1, 835 }, 836 { 837 op: "storesvc-download", 838 name: "some-snap", 839 }, 840 { 841 op: "validate-snap:Doing", 842 name: "some-snap", 843 revno: snap.R(11), 844 }, 845 { 846 op: "current", 847 old: "<no-current>", 848 }, 849 { 850 op: "open-snap-file", 851 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 852 sinfo: snap.SideInfo{ 853 RealName: "some-snap", 854 SnapID: "some-snap-id", 855 Channel: "some-channel", 856 Revision: snap.R(11), 857 }, 858 }, 859 { 860 op: "setup-snap", 861 name: "some-snap", 862 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 863 revno: snap.R(11), 864 }, 865 { 866 op: "copy-data", 867 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 868 old: "<no-old>", 869 }, 870 { 871 op: "setup-profiles:Doing", 872 name: "some-snap", 873 revno: snap.R(11), 874 }, 875 { 876 op: "candidate", 877 sinfo: snap.SideInfo{ 878 RealName: "some-snap", 879 SnapID: "some-snap-id", 880 Channel: "some-channel", 881 Revision: snap.R(11), 882 }, 883 }, 884 { 885 op: "link-snap", 886 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 887 }, 888 { 889 op: "auto-connect:Doing", 890 name: "some-snap", 891 revno: snap.R(11), 892 }, 893 { 894 op: "update-aliases", 895 }, 896 { 897 op: "cleanup-trash", 898 name: "some-snap", 899 revno: snap.R(11), 900 }, 901 } 902 // start with an easier-to-read error if this fails: 903 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 904 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 905 906 // check progress 907 ta := ts.Tasks() 908 task := ta[1] 909 _, cur, total := task.Progress() 910 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 911 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 912 c.Check(task.Summary(), Equals, `Download snap "some-snap" (11) from channel "some-channel"`) 913 914 // check install-record present 915 mountTask := ta[len(ta)-11] 916 c.Check(mountTask.Kind(), Equals, "mount-snap") 917 var installRecord backend.InstallRecord 918 c.Assert(mountTask.Get("install-record", &installRecord), IsNil) 919 c.Check(installRecord.TargetSnapExisted, Equals, false) 920 921 // check link/start snap summary 922 linkTask := ta[len(ta)-8] 923 c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (11) available to the system`) 924 startTask := ta[len(ta)-3] 925 c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (11) services`) 926 927 // verify snap-setup in the task state 928 var snapsup snapstate.SnapSetup 929 err = task.Get("snap-setup", &snapsup) 930 c.Assert(err, IsNil) 931 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 932 Channel: "some-channel", 933 UserID: s.user.ID, 934 SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 935 DownloadInfo: &snap.DownloadInfo{ 936 DownloadURL: "https://some-server.com/some/path.snap", 937 Size: 5, 938 }, 939 SideInfo: snapsup.SideInfo, 940 Type: snap.TypeApp, 941 PlugsOnly: true, 942 }) 943 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 944 RealName: "some-snap", 945 Channel: "some-channel", 946 Revision: snap.R(11), 947 SnapID: "some-snap-id", 948 }) 949 950 // verify snaps in the system state 951 var snaps map[string]*snapstate.SnapState 952 err = s.state.Get("snaps", &snaps) 953 c.Assert(err, IsNil) 954 955 snapst := snaps["some-snap"] 956 c.Assert(snapst, NotNil) 957 c.Assert(snapst.Active, Equals, true) 958 c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable") 959 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 960 RealName: "some-snap", 961 SnapID: "some-snap-id", 962 Channel: "some-channel", 963 Revision: snap.R(11), 964 }) 965 c.Assert(snapst.Required, Equals, false) 966 967 // we end with the auxiliary store info 968 c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FilePresent) 969 } 970 971 func (s *snapmgrTestSuite) TestParallelInstanceInstallRunThrough(c *C) { 972 s.state.Lock() 973 defer s.state.Unlock() 974 975 tr := config.NewTransaction(s.state) 976 tr.Set("core", "experimental.parallel-instances", true) 977 tr.Commit() 978 979 chg := s.state.NewChange("install", "install a snap") 980 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 981 ts, err := snapstate.Install(context.Background(), s.state, "some-snap_instance", opts, s.user.ID, snapstate.Flags{}) 982 c.Assert(err, IsNil) 983 chg.AddAll(ts) 984 985 s.state.Unlock() 986 s.settle(c) 987 s.state.Lock() 988 989 // ensure all our tasks ran 990 c.Assert(chg.Err(), IsNil) 991 c.Assert(chg.IsReady(), Equals, true) 992 c.Check(snapstate.Installing(s.state), Equals, false) 993 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 994 macaroon: s.user.StoreMacaroon, 995 name: "some-snap", 996 target: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"), 997 }}) 998 c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys)) 999 expected := fakeOps{ 1000 { 1001 op: "storesvc-snap-action", 1002 userID: 1, 1003 }, 1004 { 1005 op: "storesvc-snap-action:action", 1006 action: store.SnapAction{ 1007 Action: "install", 1008 InstanceName: "some-snap_instance", 1009 Channel: "some-channel", 1010 }, 1011 revno: snap.R(11), 1012 userID: 1, 1013 }, 1014 { 1015 op: "storesvc-download", 1016 name: "some-snap", 1017 }, 1018 { 1019 op: "validate-snap:Doing", 1020 name: "some-snap_instance", 1021 revno: snap.R(11), 1022 }, 1023 { 1024 op: "current", 1025 old: "<no-current>", 1026 }, 1027 { 1028 op: "open-snap-file", 1029 path: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"), 1030 sinfo: snap.SideInfo{ 1031 RealName: "some-snap", 1032 SnapID: "some-snap-id", 1033 Channel: "some-channel", 1034 Revision: snap.R(11), 1035 }, 1036 }, 1037 { 1038 op: "setup-snap", 1039 name: "some-snap_instance", 1040 path: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"), 1041 revno: snap.R(11), 1042 }, 1043 { 1044 op: "copy-data", 1045 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/11"), 1046 old: "<no-old>", 1047 }, 1048 { 1049 op: "setup-profiles:Doing", 1050 name: "some-snap_instance", 1051 revno: snap.R(11), 1052 }, 1053 { 1054 op: "candidate", 1055 sinfo: snap.SideInfo{ 1056 RealName: "some-snap", 1057 SnapID: "some-snap-id", 1058 Channel: "some-channel", 1059 Revision: snap.R(11), 1060 }, 1061 }, 1062 { 1063 op: "link-snap", 1064 path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/11"), 1065 }, 1066 { 1067 op: "auto-connect:Doing", 1068 name: "some-snap_instance", 1069 revno: snap.R(11), 1070 }, 1071 { 1072 op: "update-aliases", 1073 }, 1074 { 1075 op: "cleanup-trash", 1076 name: "some-snap_instance", 1077 revno: snap.R(11), 1078 }, 1079 } 1080 // start with an easier-to-read error if this fails: 1081 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1082 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 1083 1084 // check progress 1085 ta := ts.Tasks() 1086 task := ta[1] 1087 _, cur, total := task.Progress() 1088 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 1089 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 1090 c.Check(task.Summary(), Equals, `Download snap "some-snap_instance" (11) from channel "some-channel"`) 1091 1092 // check link/start snap summary 1093 linkTask := ta[len(ta)-8] 1094 c.Check(linkTask.Summary(), Equals, `Make snap "some-snap_instance" (11) available to the system`) 1095 startTask := ta[len(ta)-3] 1096 c.Check(startTask.Summary(), Equals, `Start snap "some-snap_instance" (11) services`) 1097 1098 // verify snap-setup in the task state 1099 var snapsup snapstate.SnapSetup 1100 err = task.Get("snap-setup", &snapsup) 1101 c.Assert(err, IsNil) 1102 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 1103 Channel: "some-channel", 1104 UserID: s.user.ID, 1105 SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"), 1106 DownloadInfo: &snap.DownloadInfo{ 1107 DownloadURL: "https://some-server.com/some/path.snap", 1108 Size: 5, 1109 }, 1110 SideInfo: snapsup.SideInfo, 1111 Type: snap.TypeApp, 1112 PlugsOnly: true, 1113 InstanceKey: "instance", 1114 }) 1115 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 1116 RealName: "some-snap", 1117 Channel: "some-channel", 1118 Revision: snap.R(11), 1119 SnapID: "some-snap-id", 1120 }) 1121 1122 // verify snaps in the system state 1123 var snaps map[string]*snapstate.SnapState 1124 err = s.state.Get("snaps", &snaps) 1125 c.Assert(err, IsNil) 1126 1127 snapst := snaps["some-snap_instance"] 1128 c.Assert(snapst, NotNil) 1129 c.Assert(snapst.Active, Equals, true) 1130 c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable") 1131 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 1132 RealName: "some-snap", 1133 SnapID: "some-snap-id", 1134 Channel: "some-channel", 1135 Revision: snap.R(11), 1136 }) 1137 c.Assert(snapst.Required, Equals, false) 1138 c.Assert(snapst.InstanceKey, Equals, "instance") 1139 1140 runHooks := tasksWithKind(ts, "run-hook") 1141 c.Assert(taskKinds(runHooks), DeepEquals, []string{"run-hook[install]", "run-hook[configure]", "run-hook[check-health]"}) 1142 for _, hookTask := range runHooks { 1143 c.Assert(hookTask.Kind(), Equals, "run-hook") 1144 var hooksup hookstate.HookSetup 1145 err = hookTask.Get("hook-setup", &hooksup) 1146 c.Assert(err, IsNil) 1147 c.Assert(hooksup.Snap, Equals, "some-snap_instance") 1148 } 1149 } 1150 1151 func (s *snapmgrTestSuite) TestInstallUndoRunThroughJustOneSnap(c *C) { 1152 s.state.Lock() 1153 defer s.state.Unlock() 1154 1155 chg := s.state.NewChange("install", "install a snap") 1156 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 1157 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 1158 c.Assert(err, IsNil) 1159 chg.AddAll(ts) 1160 1161 tasks := ts.Tasks() 1162 last := tasks[len(tasks)-1] 1163 // sanity 1164 c.Assert(last.Lanes(), HasLen, 1) 1165 terr := s.state.NewTask("error-trigger", "provoking total undo") 1166 terr.WaitFor(last) 1167 terr.JoinLane(last.Lanes()[0]) 1168 chg.AddTask(terr) 1169 1170 s.state.Unlock() 1171 defer s.se.Stop() 1172 s.settle(c) 1173 s.state.Lock() 1174 1175 mountTask := tasks[len(tasks)-11] 1176 c.Assert(mountTask.Kind(), Equals, "mount-snap") 1177 var installRecord backend.InstallRecord 1178 c.Assert(mountTask.Get("install-record", &installRecord), IsNil) 1179 c.Check(installRecord.TargetSnapExisted, Equals, false) 1180 1181 // ensure all our tasks ran 1182 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 1183 macaroon: s.user.StoreMacaroon, 1184 name: "some-snap", 1185 target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 1186 }}) 1187 expected := fakeOps{ 1188 { 1189 op: "storesvc-snap-action", 1190 userID: 1, 1191 }, 1192 { 1193 op: "storesvc-snap-action:action", 1194 action: store.SnapAction{ 1195 Action: "install", 1196 InstanceName: "some-snap", 1197 Channel: "some-channel", 1198 }, 1199 revno: snap.R(11), 1200 userID: 1, 1201 }, 1202 { 1203 op: "storesvc-download", 1204 name: "some-snap", 1205 }, 1206 { 1207 op: "validate-snap:Doing", 1208 name: "some-snap", 1209 revno: snap.R(11), 1210 }, 1211 { 1212 op: "current", 1213 old: "<no-current>", 1214 }, 1215 { 1216 op: "open-snap-file", 1217 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 1218 sinfo: snap.SideInfo{ 1219 RealName: "some-snap", 1220 SnapID: "some-snap-id", 1221 Channel: "some-channel", 1222 Revision: snap.R(11), 1223 }, 1224 }, 1225 { 1226 op: "setup-snap", 1227 name: "some-snap", 1228 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 1229 revno: snap.R(11), 1230 }, 1231 { 1232 op: "copy-data", 1233 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 1234 old: "<no-old>", 1235 }, 1236 { 1237 op: "setup-profiles:Doing", 1238 name: "some-snap", 1239 revno: snap.R(11), 1240 }, 1241 { 1242 op: "candidate", 1243 sinfo: snap.SideInfo{ 1244 RealName: "some-snap", 1245 SnapID: "some-snap-id", 1246 Channel: "some-channel", 1247 Revision: snap.R(11), 1248 }, 1249 }, 1250 { 1251 op: "link-snap", 1252 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 1253 }, 1254 { 1255 op: "auto-connect:Doing", 1256 name: "some-snap", 1257 revno: snap.R(11), 1258 }, 1259 { 1260 op: "update-aliases", 1261 }, 1262 { 1263 op: "remove-snap-aliases", 1264 name: "some-snap", 1265 }, 1266 { 1267 op: "discard-namespace", 1268 name: "some-snap", 1269 }, 1270 { 1271 op: "unlink-snap", 1272 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 1273 1274 unlinkFirstInstallUndo: true, 1275 }, 1276 { 1277 op: "setup-profiles:Undoing", 1278 name: "some-snap", 1279 revno: snap.R(11), 1280 }, 1281 { 1282 op: "undo-copy-snap-data", 1283 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 1284 old: "<no-old>", 1285 }, 1286 { 1287 op: "remove-snap-data-dir", 1288 name: "some-snap", 1289 path: filepath.Join(dirs.SnapDataDir, "some-snap"), 1290 }, 1291 { 1292 op: "undo-setup-snap", 1293 name: "some-snap", 1294 stype: "app", 1295 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 1296 }, 1297 { 1298 op: "remove-snap-dir", 1299 name: "some-snap", 1300 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 1301 }, 1302 } 1303 // start with an easier-to-read error if this fails: 1304 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1305 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 1306 } 1307 1308 func (s *snapmgrTestSuite) TestInstallWithCohortRunThrough(c *C) { 1309 s.state.Lock() 1310 defer s.state.Unlock() 1311 1312 chg := s.state.NewChange("install", "install a snap") 1313 opts := &snapstate.RevisionOptions{Channel: "some-channel", CohortKey: "scurries"} 1314 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 1315 c.Assert(err, IsNil) 1316 chg.AddAll(ts) 1317 1318 s.state.Unlock() 1319 defer s.se.Stop() 1320 s.settle(c) 1321 s.state.Lock() 1322 1323 // ensure all our tasks ran 1324 c.Assert(chg.Err(), IsNil) 1325 c.Assert(chg.IsReady(), Equals, true) 1326 c.Check(snapstate.Installing(s.state), Equals, false) 1327 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 1328 macaroon: s.user.StoreMacaroon, 1329 name: "some-snap", 1330 target: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"), 1331 }}) 1332 expected := fakeOps{ 1333 { 1334 op: "storesvc-snap-action", 1335 userID: 1, 1336 }, 1337 { 1338 op: "storesvc-snap-action:action", 1339 action: store.SnapAction{ 1340 Action: "install", 1341 InstanceName: "some-snap", 1342 CohortKey: "scurries", 1343 Channel: "some-channel", 1344 }, 1345 revno: snap.R(666), 1346 userID: 1, 1347 }, 1348 { 1349 op: "storesvc-download", 1350 name: "some-snap", 1351 }, 1352 { 1353 op: "validate-snap:Doing", 1354 name: "some-snap", 1355 revno: snap.R(666), 1356 }, 1357 { 1358 op: "current", 1359 old: "<no-current>", 1360 }, 1361 { 1362 op: "open-snap-file", 1363 path: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"), 1364 sinfo: snap.SideInfo{ 1365 RealName: "some-snap", 1366 SnapID: "some-snap-id", 1367 Revision: snap.R(666), 1368 Channel: "some-channel", 1369 }, 1370 }, 1371 { 1372 op: "setup-snap", 1373 name: "some-snap", 1374 path: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"), 1375 revno: snap.R(666), 1376 }, 1377 { 1378 op: "copy-data", 1379 path: filepath.Join(dirs.SnapMountDir, "some-snap/666"), 1380 old: "<no-old>", 1381 }, 1382 { 1383 op: "setup-profiles:Doing", 1384 name: "some-snap", 1385 revno: snap.R(666), 1386 }, 1387 { 1388 op: "candidate", 1389 sinfo: snap.SideInfo{ 1390 RealName: "some-snap", 1391 SnapID: "some-snap-id", 1392 Revision: snap.R(666), 1393 Channel: "some-channel", 1394 }, 1395 }, 1396 { 1397 op: "link-snap", 1398 path: filepath.Join(dirs.SnapMountDir, "some-snap/666"), 1399 }, 1400 { 1401 op: "auto-connect:Doing", 1402 name: "some-snap", 1403 revno: snap.R(666), 1404 }, 1405 { 1406 op: "update-aliases", 1407 }, 1408 { 1409 op: "cleanup-trash", 1410 name: "some-snap", 1411 revno: snap.R(666), 1412 }, 1413 } 1414 // start with an easier-to-read error if this fails: 1415 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1416 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 1417 1418 // check progress 1419 ta := ts.Tasks() 1420 task := ta[1] 1421 _, cur, total := task.Progress() 1422 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 1423 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 1424 c.Check(task.Summary(), Equals, `Download snap "some-snap" (666) from channel "some-channel"`) 1425 1426 // check link/start snap summary 1427 linkTask := ta[len(ta)-8] 1428 c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (666) available to the system`) 1429 startTask := ta[len(ta)-3] 1430 c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (666) services`) 1431 1432 // verify snap-setup in the task state 1433 var snapsup snapstate.SnapSetup 1434 err = task.Get("snap-setup", &snapsup) 1435 c.Assert(err, IsNil) 1436 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 1437 Channel: "some-channel", 1438 UserID: s.user.ID, 1439 SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"), 1440 DownloadInfo: &snap.DownloadInfo{ 1441 DownloadURL: "https://some-server.com/some/path.snap", 1442 Size: 5, 1443 }, 1444 SideInfo: snapsup.SideInfo, 1445 Type: snap.TypeApp, 1446 PlugsOnly: true, 1447 CohortKey: "scurries", 1448 }) 1449 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 1450 RealName: "some-snap", 1451 Revision: snap.R(666), 1452 SnapID: "some-snap-id", 1453 Channel: "some-channel", 1454 }) 1455 1456 // verify snaps in the system state 1457 var snaps map[string]*snapstate.SnapState 1458 err = s.state.Get("snaps", &snaps) 1459 c.Assert(err, IsNil) 1460 1461 snapst := snaps["some-snap"] 1462 c.Assert(snapst, NotNil) 1463 c.Assert(snapst.Active, Equals, true) 1464 c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable") 1465 c.Assert(snapst.CohortKey, Equals, "scurries") 1466 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 1467 RealName: "some-snap", 1468 SnapID: "some-snap-id", 1469 Revision: snap.R(666), 1470 Channel: "some-channel", 1471 }) 1472 c.Assert(snapst.Required, Equals, false) 1473 } 1474 1475 func (s *snapmgrTestSuite) TestInstallWithRevisionRunThrough(c *C) { 1476 s.state.Lock() 1477 defer s.state.Unlock() 1478 1479 chg := s.state.NewChange("install", "install a snap") 1480 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 1481 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 1482 c.Assert(err, IsNil) 1483 chg.AddAll(ts) 1484 1485 s.state.Unlock() 1486 defer s.se.Stop() 1487 s.settle(c) 1488 s.state.Lock() 1489 1490 // ensure all our tasks ran 1491 c.Assert(chg.Err(), IsNil) 1492 c.Assert(chg.IsReady(), Equals, true) 1493 c.Check(snapstate.Installing(s.state), Equals, false) 1494 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{ 1495 macaroon: s.user.StoreMacaroon, 1496 name: "some-snap", 1497 target: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 1498 }}) 1499 expected := fakeOps{ 1500 { 1501 op: "storesvc-snap-action", 1502 userID: 1, 1503 }, 1504 { 1505 op: "storesvc-snap-action:action", 1506 action: store.SnapAction{ 1507 Action: "install", 1508 InstanceName: "some-snap", 1509 Revision: snap.R(42), 1510 }, 1511 revno: snap.R(42), 1512 userID: 1, 1513 }, 1514 { 1515 op: "storesvc-download", 1516 name: "some-snap", 1517 }, 1518 { 1519 op: "validate-snap:Doing", 1520 name: "some-snap", 1521 revno: snap.R(42), 1522 }, 1523 { 1524 op: "current", 1525 old: "<no-current>", 1526 }, 1527 { 1528 op: "open-snap-file", 1529 path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 1530 sinfo: snap.SideInfo{ 1531 RealName: "some-snap", 1532 SnapID: "some-snap-id", 1533 Revision: snap.R(42), 1534 }, 1535 }, 1536 { 1537 op: "setup-snap", 1538 name: "some-snap", 1539 path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 1540 revno: snap.R(42), 1541 }, 1542 { 1543 op: "copy-data", 1544 path: filepath.Join(dirs.SnapMountDir, "some-snap/42"), 1545 old: "<no-old>", 1546 }, 1547 { 1548 op: "setup-profiles:Doing", 1549 name: "some-snap", 1550 revno: snap.R(42), 1551 }, 1552 { 1553 op: "candidate", 1554 sinfo: snap.SideInfo{ 1555 RealName: "some-snap", 1556 SnapID: "some-snap-id", 1557 Revision: snap.R(42), 1558 }, 1559 }, 1560 { 1561 op: "link-snap", 1562 path: filepath.Join(dirs.SnapMountDir, "some-snap/42"), 1563 }, 1564 { 1565 op: "auto-connect:Doing", 1566 name: "some-snap", 1567 revno: snap.R(42), 1568 }, 1569 { 1570 op: "update-aliases", 1571 }, 1572 { 1573 op: "cleanup-trash", 1574 name: "some-snap", 1575 revno: snap.R(42), 1576 }, 1577 } 1578 // start with an easier-to-read error if this fails: 1579 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1580 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 1581 1582 // check progress 1583 ta := ts.Tasks() 1584 task := ta[1] 1585 _, cur, total := task.Progress() 1586 c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress) 1587 c.Assert(total, Equals, s.fakeStore.fakeTotalProgress) 1588 c.Check(task.Summary(), Equals, `Download snap "some-snap" (42) from channel "some-channel"`) 1589 1590 // check link/start snap summary 1591 linkTask := ta[len(ta)-8] 1592 c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (42) available to the system`) 1593 startTask := ta[len(ta)-3] 1594 c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (42) services`) 1595 1596 // verify snap-setup in the task state 1597 var snapsup snapstate.SnapSetup 1598 err = task.Get("snap-setup", &snapsup) 1599 c.Assert(err, IsNil) 1600 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 1601 Channel: "some-channel", 1602 UserID: s.user.ID, 1603 SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 1604 DownloadInfo: &snap.DownloadInfo{ 1605 DownloadURL: "https://some-server.com/some/path.snap", 1606 Size: 5, 1607 }, 1608 SideInfo: snapsup.SideInfo, 1609 Type: snap.TypeApp, 1610 PlugsOnly: true, 1611 }) 1612 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 1613 RealName: "some-snap", 1614 Revision: snap.R(42), 1615 SnapID: "some-snap-id", 1616 }) 1617 1618 // verify snaps in the system state 1619 var snaps map[string]*snapstate.SnapState 1620 err = s.state.Get("snaps", &snaps) 1621 c.Assert(err, IsNil) 1622 1623 snapst := snaps["some-snap"] 1624 c.Assert(snapst, NotNil) 1625 c.Assert(snapst.Active, Equals, true) 1626 c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable") 1627 c.Assert(snapst.CohortKey, Equals, "") 1628 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 1629 RealName: "some-snap", 1630 SnapID: "some-snap-id", 1631 Revision: snap.R(42), 1632 }) 1633 c.Assert(snapst.Required, Equals, false) 1634 } 1635 1636 func (s *snapmgrTestSuite) TestInstallStartOrder(c *C) { 1637 s.state.Lock() 1638 defer s.state.Unlock() 1639 1640 chg := s.state.NewChange("install", "install a snap") 1641 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 1642 ts, err := snapstate.Install(context.Background(), s.state, "services-snap", opts, s.user.ID, snapstate.Flags{}) 1643 c.Assert(err, IsNil) 1644 chg.AddAll(ts) 1645 1646 s.state.Unlock() 1647 defer s.se.Stop() 1648 s.settle(c) 1649 s.state.Lock() 1650 1651 // ensure all our tasks ran 1652 c.Assert(chg.Err(), IsNil) 1653 c.Assert(chg.IsReady(), Equals, true) 1654 c.Check(snapstate.Installing(s.state), Equals, false) 1655 op := s.fakeBackend.ops.First("start-snap-services") 1656 c.Assert(op, NotNil) 1657 c.Assert(op, DeepEquals, &fakeOp{ 1658 op: "start-snap-services", 1659 path: filepath.Join(dirs.SnapMountDir, "services-snap/11"), 1660 // ordered to preserve after/before relation 1661 services: []string{"svc1", "svc3", "svc2"}, 1662 }) 1663 } 1664 1665 func (s *snapmgrTestSuite) TestInstalling(c *C) { 1666 s.state.Lock() 1667 defer s.state.Unlock() 1668 1669 c.Check(snapstate.Installing(s.state), Equals, false) 1670 1671 chg := s.state.NewChange("install", "install a snap") 1672 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 1673 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}) 1674 c.Assert(err, IsNil) 1675 chg.AddAll(ts) 1676 1677 c.Check(snapstate.Installing(s.state), Equals, true) 1678 } 1679 1680 func (s *snapmgrTestSuite) TestInstallFirstLocalRunThrough(c *C) { 1681 // use the real thing for this one 1682 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 1683 1684 restoreInstallSize := snapstate.MockInstallSize(func(st *state.State, snaps []*snap.Info, userID int) (uint64, error) { 1685 c.Fatalf("installSize shouldn't be hit with local install") 1686 return 0, nil 1687 }) 1688 defer restoreInstallSize() 1689 1690 s.state.Lock() 1691 defer s.state.Unlock() 1692 1693 mockSnap := makeTestSnap(c, `name: mock 1694 version: 1.0`) 1695 chg := s.state.NewChange("install", "install a local snap") 1696 ts, info, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{}) 1697 c.Assert(err, IsNil) 1698 chg.AddAll(ts) 1699 1700 // ensure the returned info is correct 1701 c.Check(info.SideInfo.RealName, Equals, "mock") 1702 c.Check(info.Version, Equals, "1.0") 1703 1704 s.state.Unlock() 1705 defer s.se.Stop() 1706 s.settle(c) 1707 s.state.Lock() 1708 1709 expected := fakeOps{ 1710 { 1711 // only local install was run, i.e. first actions are pseudo-action current 1712 op: "current", 1713 old: "<no-current>", 1714 }, 1715 { 1716 // and setup-snap 1717 op: "setup-snap", 1718 name: "mock", 1719 path: mockSnap, 1720 revno: snap.R("x1"), 1721 }, 1722 { 1723 op: "copy-data", 1724 path: filepath.Join(dirs.SnapMountDir, "mock/x1"), 1725 old: "<no-old>", 1726 }, 1727 { 1728 op: "setup-profiles:Doing", 1729 name: "mock", 1730 revno: snap.R("x1"), 1731 }, 1732 { 1733 op: "candidate", 1734 sinfo: snap.SideInfo{ 1735 RealName: "mock", 1736 Revision: snap.R("x1"), 1737 }, 1738 }, 1739 { 1740 op: "link-snap", 1741 path: filepath.Join(dirs.SnapMountDir, "mock/x1"), 1742 }, 1743 { 1744 op: "auto-connect:Doing", 1745 name: "mock", 1746 revno: snap.R("x1"), 1747 }, 1748 { 1749 op: "update-aliases", 1750 }, 1751 { 1752 op: "cleanup-trash", 1753 name: "mock", 1754 revno: snap.R("x1"), 1755 }, 1756 } 1757 1758 // start with an easier-to-read error if this fails: 1759 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1760 c.Check(s.fakeBackend.ops, DeepEquals, expected) 1761 1762 // verify snapSetup info 1763 var snapsup snapstate.SnapSetup 1764 task := ts.Tasks()[1] 1765 err = task.Get("snap-setup", &snapsup) 1766 c.Assert(err, IsNil) 1767 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 1768 SnapPath: mockSnap, 1769 SideInfo: snapsup.SideInfo, 1770 Type: snap.TypeApp, 1771 PlugsOnly: true, 1772 }) 1773 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 1774 RealName: "mock", 1775 Revision: snap.R(-1), 1776 }) 1777 1778 // verify snaps in the system state 1779 var snapst snapstate.SnapState 1780 err = snapstate.Get(s.state, "mock", &snapst) 1781 c.Assert(err, IsNil) 1782 1783 c.Assert(snapst.Active, Equals, true) 1784 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 1785 RealName: "mock", 1786 Channel: "", 1787 Revision: snap.R(-1), 1788 }) 1789 c.Assert(snapst.LocalRevision(), Equals, snap.R(-1)) 1790 } 1791 1792 func (s *snapmgrTestSuite) TestInstallSubsequentLocalRunThrough(c *C) { 1793 // use the real thing for this one 1794 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 1795 1796 s.state.Lock() 1797 defer s.state.Unlock() 1798 1799 snapstate.Set(s.state, "mock", &snapstate.SnapState{ 1800 Active: true, 1801 Sequence: []*snap.SideInfo{ 1802 {RealName: "mock", Revision: snap.R(-2)}, 1803 }, 1804 Current: snap.R(-2), 1805 SnapType: "app", 1806 }) 1807 1808 mockSnap := makeTestSnap(c, `name: mock 1809 version: 1.0 1810 epoch: 1* 1811 `) 1812 chg := s.state.NewChange("install", "install a local snap") 1813 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{}) 1814 c.Assert(err, IsNil) 1815 chg.AddAll(ts) 1816 1817 s.state.Unlock() 1818 defer s.se.Stop() 1819 s.settle(c) 1820 s.state.Lock() 1821 1822 expected := fakeOps{ 1823 { 1824 op: "current", 1825 old: filepath.Join(dirs.SnapMountDir, "mock/x2"), 1826 }, 1827 { 1828 op: "setup-snap", 1829 name: "mock", 1830 path: mockSnap, 1831 revno: snap.R("x3"), 1832 }, 1833 { 1834 op: "remove-snap-aliases", 1835 name: "mock", 1836 }, 1837 { 1838 op: "unlink-snap", 1839 path: filepath.Join(dirs.SnapMountDir, "mock/x2"), 1840 }, 1841 { 1842 op: "copy-data", 1843 path: filepath.Join(dirs.SnapMountDir, "mock/x3"), 1844 old: filepath.Join(dirs.SnapMountDir, "mock/x2"), 1845 }, 1846 { 1847 op: "setup-profiles:Doing", 1848 name: "mock", 1849 revno: snap.R(-3), 1850 }, 1851 { 1852 op: "candidate", 1853 sinfo: snap.SideInfo{ 1854 RealName: "mock", 1855 Revision: snap.R(-3), 1856 }, 1857 }, 1858 { 1859 op: "link-snap", 1860 path: filepath.Join(dirs.SnapMountDir, "mock/x3"), 1861 }, 1862 { 1863 op: "auto-connect:Doing", 1864 name: "mock", 1865 revno: snap.R("x3"), 1866 }, 1867 { 1868 op: "update-aliases", 1869 }, 1870 { 1871 op: "cleanup-trash", 1872 name: "mock", 1873 revno: snap.R("x3"), 1874 }, 1875 } 1876 1877 // start with an easier-to-read error if this fails: 1878 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1879 c.Check(s.fakeBackend.ops, DeepEquals, expected) 1880 1881 // verify snapSetup info 1882 var snapsup snapstate.SnapSetup 1883 task := ts.Tasks()[1] 1884 err = task.Get("snap-setup", &snapsup) 1885 c.Assert(err, IsNil) 1886 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 1887 SnapPath: mockSnap, 1888 SideInfo: snapsup.SideInfo, 1889 Type: snap.TypeApp, 1890 PlugsOnly: true, 1891 }) 1892 c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 1893 RealName: "mock", 1894 Revision: snap.R(-3), 1895 }) 1896 1897 // verify snaps in the system state 1898 var snapst snapstate.SnapState 1899 err = snapstate.Get(s.state, "mock", &snapst) 1900 c.Assert(err, IsNil) 1901 1902 c.Assert(snapst.Active, Equals, true) 1903 c.Assert(snapst.Sequence, HasLen, 2) 1904 c.Assert(snapst.CurrentSideInfo(), DeepEquals, &snap.SideInfo{ 1905 RealName: "mock", 1906 Channel: "", 1907 Revision: snap.R(-3), 1908 }) 1909 c.Assert(snapst.LocalRevision(), Equals, snap.R(-3)) 1910 } 1911 1912 func (s *snapmgrTestSuite) TestInstallOldSubsequentLocalRunThrough(c *C) { 1913 // use the real thing for this one 1914 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 1915 1916 s.state.Lock() 1917 defer s.state.Unlock() 1918 1919 snapstate.Set(s.state, "mock", &snapstate.SnapState{ 1920 Active: true, 1921 Sequence: []*snap.SideInfo{ 1922 {RealName: "mock", Revision: snap.R(100001)}, 1923 }, 1924 Current: snap.R(100001), 1925 SnapType: "app", 1926 }) 1927 1928 mockSnap := makeTestSnap(c, `name: mock 1929 version: 1.0 1930 epoch: 1* 1931 `) 1932 chg := s.state.NewChange("install", "install a local snap") 1933 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{}) 1934 c.Assert(err, IsNil) 1935 chg.AddAll(ts) 1936 1937 s.state.Unlock() 1938 defer s.se.Stop() 1939 s.settle(c) 1940 s.state.Lock() 1941 1942 expected := fakeOps{ 1943 { 1944 // ensure only local install was run, i.e. first action is pseudo-action current 1945 op: "current", 1946 old: filepath.Join(dirs.SnapMountDir, "mock/100001"), 1947 }, 1948 { 1949 // and setup-snap 1950 op: "setup-snap", 1951 name: "mock", 1952 path: mockSnap, 1953 revno: snap.R("x1"), 1954 }, 1955 { 1956 op: "remove-snap-aliases", 1957 name: "mock", 1958 }, 1959 { 1960 op: "unlink-snap", 1961 path: filepath.Join(dirs.SnapMountDir, "mock/100001"), 1962 }, 1963 { 1964 op: "copy-data", 1965 path: filepath.Join(dirs.SnapMountDir, "mock/x1"), 1966 old: filepath.Join(dirs.SnapMountDir, "mock/100001"), 1967 }, 1968 { 1969 op: "setup-profiles:Doing", 1970 name: "mock", 1971 revno: snap.R("x1"), 1972 }, 1973 { 1974 op: "candidate", 1975 sinfo: snap.SideInfo{ 1976 RealName: "mock", 1977 Revision: snap.R("x1"), 1978 }, 1979 }, 1980 { 1981 op: "link-snap", 1982 path: filepath.Join(dirs.SnapMountDir, "mock/x1"), 1983 }, 1984 { 1985 op: "auto-connect:Doing", 1986 name: "mock", 1987 revno: snap.R("x1"), 1988 }, 1989 { 1990 op: "update-aliases", 1991 }, 1992 { 1993 // and cleanup 1994 op: "cleanup-trash", 1995 name: "mock", 1996 revno: snap.R("x1"), 1997 }, 1998 } 1999 // start with an easier-to-read error if this fails: 2000 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2001 c.Check(s.fakeBackend.ops, DeepEquals, expected) 2002 2003 var snapst snapstate.SnapState 2004 err = snapstate.Get(s.state, "mock", &snapst) 2005 c.Assert(err, IsNil) 2006 2007 c.Assert(snapst.Active, Equals, true) 2008 c.Assert(snapst.Sequence, HasLen, 2) 2009 c.Assert(snapst.CurrentSideInfo(), DeepEquals, &snap.SideInfo{ 2010 RealName: "mock", 2011 Channel: "", 2012 Revision: snap.R(-1), 2013 }) 2014 c.Assert(snapst.LocalRevision(), Equals, snap.R(-1)) 2015 } 2016 2017 func (s *snapmgrTestSuite) TestInstallPathWithMetadataRunThrough(c *C) { 2018 // use the real thing for this one 2019 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 2020 2021 s.state.Lock() 2022 defer s.state.Unlock() 2023 2024 someSnap := makeTestSnap(c, `name: orig-name 2025 version: 1.0`) 2026 chg := s.state.NewChange("install", "install a local snap") 2027 2028 si := &snap.SideInfo{ 2029 RealName: "some-snap", 2030 SnapID: "some-snap-id", 2031 Revision: snap.R(42), 2032 } 2033 ts, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "", snapstate.Flags{Required: true}) 2034 c.Assert(err, IsNil) 2035 chg.AddAll(ts) 2036 2037 s.state.Unlock() 2038 defer s.se.Stop() 2039 s.settle(c) 2040 s.state.Lock() 2041 2042 // ensure only local install was run, i.e. first actions are pseudo-action current 2043 c.Assert(s.fakeBackend.ops.Ops(), HasLen, 9) 2044 c.Check(s.fakeBackend.ops[0].op, Equals, "current") 2045 c.Check(s.fakeBackend.ops[0].old, Equals, "<no-current>") 2046 // and setup-snap 2047 c.Check(s.fakeBackend.ops[1].op, Equals, "setup-snap") 2048 c.Check(s.fakeBackend.ops[1].name, Equals, "some-snap") 2049 c.Check(s.fakeBackend.ops[1].path, Matches, `.*/orig-name_1.0_all.snap`) 2050 c.Check(s.fakeBackend.ops[1].revno, Equals, snap.R(42)) 2051 2052 c.Check(s.fakeBackend.ops[4].op, Equals, "candidate") 2053 c.Check(s.fakeBackend.ops[4].sinfo, DeepEquals, *si) 2054 c.Check(s.fakeBackend.ops[5].op, Equals, "link-snap") 2055 c.Check(s.fakeBackend.ops[5].path, Equals, filepath.Join(dirs.SnapMountDir, "some-snap/42")) 2056 2057 // verify snapSetup info 2058 var snapsup snapstate.SnapSetup 2059 task := ts.Tasks()[0] 2060 err = task.Get("snap-setup", &snapsup) 2061 c.Assert(err, IsNil) 2062 c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{ 2063 SnapPath: someSnap, 2064 SideInfo: snapsup.SideInfo, 2065 Flags: snapstate.Flags{ 2066 Required: true, 2067 }, 2068 Type: snap.TypeApp, 2069 PlugsOnly: true, 2070 }) 2071 c.Assert(snapsup.SideInfo, DeepEquals, si) 2072 2073 // verify snaps in the system state 2074 var snapst snapstate.SnapState 2075 err = snapstate.Get(s.state, "some-snap", &snapst) 2076 c.Assert(err, IsNil) 2077 2078 c.Assert(snapst.Active, Equals, true) 2079 c.Assert(snapst.TrackingChannel, Equals, "") 2080 c.Assert(snapst.Sequence[0], DeepEquals, si) 2081 c.Assert(snapst.LocalRevision().Unset(), Equals, true) 2082 c.Assert(snapst.Required, Equals, true) 2083 } 2084 2085 func (s *snapmgrTestSuite) TestInstallPathSkipConfigure(c *C) { 2086 r := release.MockOnClassic(false) 2087 defer r() 2088 2089 makeInstalledMockCoreSnap(c) 2090 2091 // using MockSnap, we want to read the bits on disk 2092 snapstate.MockSnapReadInfo(snap.ReadInfo) 2093 2094 s.state.Lock() 2095 defer s.state.Unlock() 2096 2097 s.prepareGadget(c) 2098 2099 snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0") 2100 2101 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{SkipConfigure: true}) 2102 c.Assert(err, IsNil) 2103 2104 snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0]) 2105 c.Assert(err, IsNil) 2106 // SkipConfigure is consumed and consulted when creating the taskset 2107 // but is not copied into SnapSetup 2108 c.Check(snapsup.Flags.SkipConfigure, Equals, false) 2109 } 2110 2111 func (s *snapmgrTestSuite) TestInstallWithoutCoreRunThrough1(c *C) { 2112 s.state.Lock() 2113 defer s.state.Unlock() 2114 2115 // pretend we don't have core 2116 snapstate.Set(s.state, "core", nil) 2117 2118 chg := s.state.NewChange("install", "install a snap on a system without core") 2119 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 2120 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2121 c.Assert(err, IsNil) 2122 chg.AddAll(ts) 2123 2124 s.state.Unlock() 2125 defer s.se.Stop() 2126 s.settle(c) 2127 s.state.Lock() 2128 2129 // ensure all our tasks ran 2130 c.Assert(chg.Err(), IsNil) 2131 c.Assert(chg.IsReady(), Equals, true) 2132 c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{ 2133 { 2134 macaroon: s.user.StoreMacaroon, 2135 name: "core", 2136 target: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 2137 }, 2138 { 2139 macaroon: s.user.StoreMacaroon, 2140 name: "some-snap", 2141 target: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 2142 }}) 2143 expected := fakeOps{ 2144 // we check the snap 2145 { 2146 op: "storesvc-snap-action", 2147 userID: 1, 2148 }, 2149 { 2150 op: "storesvc-snap-action:action", 2151 action: store.SnapAction{ 2152 Action: "install", 2153 InstanceName: "some-snap", 2154 Revision: snap.R(42), 2155 }, 2156 revno: snap.R(42), 2157 userID: 1, 2158 }, 2159 // then we check core because its not installed already 2160 // and continue with that 2161 { 2162 op: "storesvc-snap-action", 2163 userID: 1, 2164 }, 2165 { 2166 op: "storesvc-snap-action:action", 2167 action: store.SnapAction{ 2168 Action: "install", 2169 InstanceName: "core", 2170 Channel: "stable", 2171 }, 2172 revno: snap.R(11), 2173 userID: 1, 2174 }, 2175 { 2176 op: "storesvc-download", 2177 name: "core", 2178 }, 2179 { 2180 op: "validate-snap:Doing", 2181 name: "core", 2182 revno: snap.R(11), 2183 }, 2184 { 2185 op: "current", 2186 old: "<no-current>", 2187 }, 2188 { 2189 op: "open-snap-file", 2190 path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 2191 sinfo: snap.SideInfo{ 2192 RealName: "core", 2193 Channel: "stable", 2194 SnapID: "core-id", 2195 Revision: snap.R(11), 2196 }, 2197 }, 2198 { 2199 op: "setup-snap", 2200 name: "core", 2201 path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"), 2202 revno: snap.R(11), 2203 }, 2204 { 2205 op: "copy-data", 2206 path: filepath.Join(dirs.SnapMountDir, "core/11"), 2207 old: "<no-old>", 2208 }, 2209 { 2210 op: "setup-profiles:Doing", 2211 name: "core", 2212 revno: snap.R(11), 2213 }, 2214 { 2215 op: "candidate", 2216 sinfo: snap.SideInfo{ 2217 RealName: "core", 2218 Channel: "stable", 2219 SnapID: "core-id", 2220 Revision: snap.R(11), 2221 }, 2222 }, 2223 { 2224 op: "link-snap", 2225 path: filepath.Join(dirs.SnapMountDir, "core/11"), 2226 }, 2227 { 2228 op: "auto-connect:Doing", 2229 name: "core", 2230 revno: snap.R(11), 2231 }, 2232 { 2233 op: "update-aliases", 2234 }, 2235 // after core is in place continue with the snap 2236 { 2237 op: "storesvc-download", 2238 name: "some-snap", 2239 }, 2240 { 2241 op: "validate-snap:Doing", 2242 name: "some-snap", 2243 revno: snap.R(42), 2244 }, 2245 { 2246 op: "current", 2247 old: "<no-current>", 2248 }, 2249 { 2250 op: "open-snap-file", 2251 path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 2252 sinfo: snap.SideInfo{ 2253 RealName: "some-snap", 2254 SnapID: "some-snap-id", 2255 Revision: snap.R(42), 2256 }, 2257 }, 2258 { 2259 op: "setup-snap", 2260 name: "some-snap", 2261 path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"), 2262 revno: snap.R(42), 2263 }, 2264 { 2265 op: "copy-data", 2266 path: filepath.Join(dirs.SnapMountDir, "some-snap/42"), 2267 old: "<no-old>", 2268 }, 2269 { 2270 op: "setup-profiles:Doing", 2271 name: "some-snap", 2272 revno: snap.R(42), 2273 }, 2274 { 2275 op: "candidate", 2276 sinfo: snap.SideInfo{ 2277 RealName: "some-snap", 2278 SnapID: "some-snap-id", 2279 Revision: snap.R(42), 2280 }, 2281 }, 2282 { 2283 op: "link-snap", 2284 path: filepath.Join(dirs.SnapMountDir, "some-snap/42"), 2285 }, 2286 { 2287 op: "auto-connect:Doing", 2288 name: "some-snap", 2289 revno: snap.R(42), 2290 }, 2291 { 2292 op: "update-aliases", 2293 }, 2294 // cleanups order is random 2295 { 2296 op: "cleanup-trash", 2297 name: "core", 2298 revno: snap.R(42), 2299 }, 2300 { 2301 op: "cleanup-trash", 2302 name: "some-snap", 2303 revno: snap.R(42), 2304 }, 2305 } 2306 // start with an easier-to-read error if this fails: 2307 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2308 // compare the details without the cleanup tasks, the order is random 2309 // as they run in parallel 2310 opsLenWithoutCleanups := len(s.fakeBackend.ops) - 2 2311 c.Assert(s.fakeBackend.ops[:opsLenWithoutCleanups], DeepEquals, expected[:opsLenWithoutCleanups]) 2312 2313 // verify core in the system state 2314 var snaps map[string]*snapstate.SnapState 2315 err = s.state.Get("snaps", &snaps) 2316 c.Assert(err, IsNil) 2317 2318 snapst := snaps["core"] 2319 c.Assert(snapst, NotNil) 2320 c.Assert(snapst.Active, Equals, true) 2321 c.Assert(snapst.TrackingChannel, Equals, "latest/stable") 2322 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 2323 RealName: "core", 2324 Channel: "stable", 2325 SnapID: "core-id", 2326 Revision: snap.R(11), 2327 }) 2328 } 2329 2330 func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsRunThrough(c *C) { 2331 s.state.Lock() 2332 defer s.state.Unlock() 2333 2334 restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond) 2335 defer restore() 2336 2337 // pretend we don't have core 2338 snapstate.Set(s.state, "core", nil) 2339 2340 chg1 := s.state.NewChange("install", "install snap 1") 2341 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 2342 ts1, err := snapstate.Install(context.Background(), s.state, "snap1", opts, s.user.ID, snapstate.Flags{}) 2343 c.Assert(err, IsNil) 2344 chg1.AddAll(ts1) 2345 2346 chg2 := s.state.NewChange("install", "install snap 2") 2347 opts = &snapstate.RevisionOptions{Channel: "some-other-channel", Revision: snap.R(21)} 2348 ts2, err := snapstate.Install(context.Background(), s.state, "snap2", opts, s.user.ID, snapstate.Flags{}) 2349 c.Assert(err, IsNil) 2350 chg2.AddAll(ts2) 2351 2352 s.state.Unlock() 2353 defer s.se.Stop() 2354 s.settle(c) 2355 s.state.Lock() 2356 2357 // ensure all our tasks ran and core was only installed once 2358 c.Assert(chg1.Err(), IsNil) 2359 c.Assert(chg2.Err(), IsNil) 2360 2361 c.Assert(chg1.IsReady(), Equals, true) 2362 c.Assert(chg2.IsReady(), Equals, true) 2363 2364 // order in which the changes run is random 2365 if len(chg1.Tasks()) < len(chg2.Tasks()) { 2366 chg1, chg2 = chg2, chg1 2367 } 2368 c.Assert(taskKinds(chg1.Tasks()), HasLen, 28) 2369 c.Assert(taskKinds(chg2.Tasks()), HasLen, 14) 2370 2371 // FIXME: add helpers and do a DeepEquals here for the operations 2372 } 2373 2374 func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsWithFailureRunThrough(c *C) { 2375 s.state.Lock() 2376 defer s.state.Unlock() 2377 2378 // slightly longer retry timeout to avoid deadlock when we 2379 // trigger a retry quickly that the link snap for core does 2380 // not have a chance to run 2381 restore := snapstate.MockPrerequisitesRetryTimeout(40 * time.Millisecond) 2382 defer restore() 2383 2384 defer s.se.Stop() 2385 // Two changes are created, the first will fails, the second will 2386 // be fine. The order of what change runs first is random, the 2387 // first change will also install core in its own lane. This test 2388 // ensures that core gets installed and there are no conflicts 2389 // even if core already got installed from the first change. 2390 // 2391 // It runs multiple times so that both possible cases get a chance 2392 // to run 2393 for i := 0; i < 5; i++ { 2394 // start clean 2395 snapstate.Set(s.state, "core", nil) 2396 snapstate.Set(s.state, "snap2", nil) 2397 2398 // chg1 has an error 2399 chg1 := s.state.NewChange("install", "install snap 1") 2400 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 2401 ts1, err := snapstate.Install(context.Background(), s.state, "snap1", opts, s.user.ID, snapstate.Flags{}) 2402 c.Assert(err, IsNil) 2403 chg1.AddAll(ts1) 2404 2405 tasks := ts1.Tasks() 2406 last := tasks[len(tasks)-1] 2407 terr := s.state.NewTask("error-trigger", "provoking total undo") 2408 terr.WaitFor(last) 2409 chg1.AddTask(terr) 2410 2411 // chg2 is good 2412 chg2 := s.state.NewChange("install", "install snap 2") 2413 opts = &snapstate.RevisionOptions{Channel: "some-other-channel", Revision: snap.R(21)} 2414 ts2, err := snapstate.Install(context.Background(), s.state, "snap2", opts, s.user.ID, snapstate.Flags{}) 2415 c.Assert(err, IsNil) 2416 chg2.AddAll(ts2) 2417 2418 // we use our own settle as we need a bigger timeout 2419 s.state.Unlock() 2420 err = s.o.Settle(testutil.HostScaledTimeout(15 * time.Second)) 2421 s.state.Lock() 2422 c.Assert(err, IsNil) 2423 2424 // ensure expected change states 2425 c.Check(chg1.Status(), Equals, state.ErrorStatus) 2426 c.Check(chg2.Status(), Equals, state.DoneStatus) 2427 2428 // ensure we have both core and snap2 2429 var snapst snapstate.SnapState 2430 err = snapstate.Get(s.state, "core", &snapst) 2431 c.Assert(err, IsNil) 2432 c.Assert(snapst.Active, Equals, true) 2433 c.Assert(snapst.Sequence, HasLen, 1) 2434 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 2435 RealName: "core", 2436 SnapID: "core-id", 2437 Channel: "stable", 2438 Revision: snap.R(11), 2439 }) 2440 2441 var snapst2 snapstate.SnapState 2442 err = snapstate.Get(s.state, "snap2", &snapst2) 2443 c.Assert(err, IsNil) 2444 c.Assert(snapst2.Active, Equals, true) 2445 c.Assert(snapst2.Sequence, HasLen, 1) 2446 c.Assert(snapst2.Sequence[0], DeepEquals, &snap.SideInfo{ 2447 RealName: "snap2", 2448 SnapID: "snap2-id", 2449 Channel: "", 2450 Revision: snap.R(21), 2451 }) 2452 2453 } 2454 } 2455 2456 type behindYourBackStore struct { 2457 *fakeStore 2458 state *state.State 2459 2460 coreInstallRequested bool 2461 coreInstalled bool 2462 chg *state.Change 2463 } 2464 2465 func (s behindYourBackStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) { 2466 if assertQuery != nil { 2467 panic("no assertion query support") 2468 } 2469 2470 if len(actions) == 1 && actions[0].Action == "install" && actions[0].InstanceName == "core" { 2471 s.state.Lock() 2472 if !s.coreInstallRequested { 2473 s.coreInstallRequested = true 2474 snapsup := &snapstate.SnapSetup{ 2475 SideInfo: &snap.SideInfo{ 2476 RealName: "core", 2477 }, 2478 } 2479 t := s.state.NewTask("prepare", "prepare core") 2480 t.Set("snap-setup", snapsup) 2481 s.chg = s.state.NewChange("install", "install core") 2482 s.chg.AddAll(state.NewTaskSet(t)) 2483 } 2484 if s.chg != nil && !s.coreInstalled { 2485 // marks change ready but also 2486 // tasks need to also be marked cleaned 2487 for _, t := range s.chg.Tasks() { 2488 t.SetStatus(state.DoneStatus) 2489 t.SetClean() 2490 } 2491 snapstate.Set(s.state, "core", &snapstate.SnapState{ 2492 Active: true, 2493 Sequence: []*snap.SideInfo{ 2494 {RealName: "core", Revision: snap.R(1)}, 2495 }, 2496 Current: snap.R(1), 2497 SnapType: "os", 2498 }) 2499 s.coreInstalled = true 2500 } 2501 s.state.Unlock() 2502 } 2503 2504 return s.fakeStore.SnapAction(ctx, currentSnaps, actions, nil, user, opts) 2505 } 2506 2507 // this test the scenario that some-snap gets installed and during the 2508 // install (when unlocking for the store info call for core) an 2509 // explicit "snap install core" happens. In this case the snapstate 2510 // will return a change conflict. we handle this via a retry, ensure 2511 // this is actually what happens. 2512 func (s *snapmgrTestSuite) TestInstallWithoutCoreConflictingInstall(c *C) { 2513 s.state.Lock() 2514 defer s.state.Unlock() 2515 2516 restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond) 2517 defer restore() 2518 2519 snapstate.ReplaceStore(s.state, behindYourBackStore{fakeStore: s.fakeStore, state: s.state}) 2520 2521 // pretend we don't have core 2522 snapstate.Set(s.state, "core", nil) 2523 2524 // now install a snap that will pull in core 2525 chg := s.state.NewChange("install", "install a snap on a system without core") 2526 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 2527 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2528 c.Assert(err, IsNil) 2529 chg.AddAll(ts) 2530 2531 prereq := ts.Tasks()[0] 2532 c.Assert(prereq.Kind(), Equals, "prerequisites") 2533 c.Check(prereq.AtTime().IsZero(), Equals, true) 2534 2535 s.state.Unlock() 2536 defer s.se.Stop() 2537 2538 // start running the change, this will trigger the 2539 // prerequisites task, which will trigger the install of core 2540 // and also call our mock store which will generate a parallel 2541 // change 2542 s.se.Ensure() 2543 s.se.Wait() 2544 2545 // change is not ready yet, because the prerequists triggered 2546 // a state.Retry{} because of the conflicting change 2547 c.Assert(chg.IsReady(), Equals, false) 2548 s.state.Lock() 2549 // marked for retry 2550 c.Check(prereq.AtTime().IsZero(), Equals, false) 2551 c.Check(prereq.Status().Ready(), Equals, false) 2552 s.state.Unlock() 2553 2554 // retry interval is 10ms so 20ms should be plenty of time 2555 time.Sleep(20 * time.Millisecond) 2556 s.settle(c) 2557 // chg got retried, core is now installed, things are good 2558 c.Assert(chg.IsReady(), Equals, true) 2559 2560 s.state.Lock() 2561 2562 // ensure all our tasks ran 2563 c.Assert(chg.Err(), IsNil) 2564 c.Assert(chg.IsReady(), Equals, true) 2565 2566 // verify core in the system state 2567 var snaps map[string]*snapstate.SnapState 2568 err = s.state.Get("snaps", &snaps) 2569 c.Assert(err, IsNil) 2570 2571 snapst := snaps["core"] 2572 c.Assert(snapst, NotNil) 2573 c.Assert(snapst.Active, Equals, true) 2574 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 2575 RealName: "core", 2576 Revision: snap.R(1), 2577 }) 2578 2579 snapst = snaps["some-snap"] 2580 c.Assert(snapst, NotNil) 2581 c.Assert(snapst.Active, Equals, true) 2582 c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{ 2583 RealName: "some-snap", 2584 SnapID: "some-snap-id", 2585 Channel: "some-channel", 2586 Revision: snap.R(11), 2587 }) 2588 } 2589 2590 func (s *snapmgrTestSuite) TestInstallDefaultProviderRunThrough(c *C) { 2591 s.state.Lock() 2592 defer s.state.Unlock() 2593 2594 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 2595 2596 repo := interfaces.NewRepository() 2597 ifacerepo.Replace(s.state, repo) 2598 2599 chg := s.state.NewChange("install", "install a snap") 2600 opts := &snapstate.RevisionOptions{Channel: "stable", Revision: snap.R(42)} 2601 ts, err := snapstate.Install(context.Background(), s.state, "snap-content-plug", opts, s.user.ID, snapstate.Flags{}) 2602 c.Assert(err, IsNil) 2603 chg.AddAll(ts) 2604 2605 s.state.Unlock() 2606 defer s.se.Stop() 2607 s.settle(c) 2608 s.state.Lock() 2609 2610 // ensure all our tasks ran 2611 c.Assert(chg.Err(), IsNil) 2612 c.Assert(chg.IsReady(), Equals, true) 2613 expected := fakeOps{{ 2614 op: "storesvc-snap-action", 2615 userID: 1, 2616 }, { 2617 op: "storesvc-snap-action:action", 2618 action: store.SnapAction{ 2619 Action: "install", 2620 InstanceName: "snap-content-plug", 2621 Revision: snap.R(42), 2622 }, 2623 revno: snap.R(42), 2624 userID: 1, 2625 }, { 2626 op: "storesvc-snap-action", 2627 userID: 1, 2628 }, { 2629 op: "storesvc-snap-action:action", 2630 action: store.SnapAction{ 2631 Action: "install", 2632 InstanceName: "snap-content-slot", 2633 Channel: "stable", 2634 }, 2635 revno: snap.R(11), 2636 userID: 1, 2637 }, { 2638 op: "storesvc-download", 2639 name: "snap-content-slot", 2640 }, { 2641 op: "validate-snap:Doing", 2642 name: "snap-content-slot", 2643 revno: snap.R(11), 2644 }, { 2645 op: "current", 2646 old: "<no-current>", 2647 }, { 2648 op: "open-snap-file", 2649 path: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap"), 2650 sinfo: snap.SideInfo{ 2651 RealName: "snap-content-slot", 2652 Channel: "stable", 2653 SnapID: "snap-content-slot-id", 2654 Revision: snap.R(11), 2655 }, 2656 }, { 2657 op: "setup-snap", 2658 name: "snap-content-slot", 2659 path: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap"), 2660 revno: snap.R(11), 2661 }, { 2662 op: "copy-data", 2663 path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"), 2664 old: "<no-old>", 2665 }, { 2666 op: "setup-profiles:Doing", 2667 name: "snap-content-slot", 2668 revno: snap.R(11), 2669 }, { 2670 op: "candidate", 2671 sinfo: snap.SideInfo{ 2672 RealName: "snap-content-slot", 2673 Channel: "stable", 2674 SnapID: "snap-content-slot-id", 2675 Revision: snap.R(11), 2676 }, 2677 }, { 2678 op: "link-snap", 2679 path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"), 2680 }, { 2681 op: "auto-connect:Doing", 2682 name: "snap-content-slot", 2683 revno: snap.R(11), 2684 }, { 2685 op: "update-aliases", 2686 }, { 2687 op: "storesvc-download", 2688 name: "snap-content-plug", 2689 }, { 2690 op: "validate-snap:Doing", 2691 name: "snap-content-plug", 2692 revno: snap.R(42), 2693 }, { 2694 op: "current", 2695 old: "<no-current>", 2696 }, { 2697 op: "open-snap-file", 2698 path: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_42.snap"), 2699 sinfo: snap.SideInfo{ 2700 RealName: "snap-content-plug", 2701 SnapID: "snap-content-plug-id", 2702 Revision: snap.R(42), 2703 }, 2704 }, { 2705 op: "setup-snap", 2706 name: "snap-content-plug", 2707 path: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_42.snap"), 2708 revno: snap.R(42), 2709 }, { 2710 op: "copy-data", 2711 path: filepath.Join(dirs.SnapMountDir, "snap-content-plug/42"), 2712 old: "<no-old>", 2713 }, { 2714 op: "setup-profiles:Doing", 2715 name: "snap-content-plug", 2716 revno: snap.R(42), 2717 }, { 2718 op: "candidate", 2719 sinfo: snap.SideInfo{ 2720 RealName: "snap-content-plug", 2721 SnapID: "snap-content-plug-id", 2722 Revision: snap.R(42), 2723 }, 2724 }, { 2725 op: "link-snap", 2726 path: filepath.Join(dirs.SnapMountDir, "snap-content-plug/42"), 2727 }, { 2728 op: "auto-connect:Doing", 2729 name: "snap-content-plug", 2730 revno: snap.R(42), 2731 }, { 2732 op: "update-aliases", 2733 }, { 2734 op: "cleanup-trash", 2735 name: "snap-content-plug", 2736 revno: snap.R(42), 2737 }, { 2738 op: "cleanup-trash", 2739 name: "snap-content-slot", 2740 revno: snap.R(11), 2741 }, 2742 } 2743 // snap and default provider are installed in parallel so we can't 2744 // do a simple c.Check(ops, DeepEquals, fakeOps{...}) 2745 c.Check(len(s.fakeBackend.ops), Equals, len(expected)) 2746 for _, op := range expected { 2747 c.Assert(s.fakeBackend.ops, testutil.DeepContains, op) 2748 } 2749 for _, op := range s.fakeBackend.ops { 2750 c.Assert(expected, testutil.DeepContains, op) 2751 } 2752 } 2753 2754 func (s *snapmgrTestSuite) TestInstallDefaultProviderCompat(c *C) { 2755 s.state.Lock() 2756 defer s.state.Unlock() 2757 2758 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 2759 2760 repo := interfaces.NewRepository() 2761 ifacerepo.Replace(s.state, repo) 2762 2763 chg := s.state.NewChange("install", "install a snap") 2764 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 2765 ts, err := snapstate.Install(context.Background(), s.state, "snap-content-plug-compat", opts, s.user.ID, snapstate.Flags{}) 2766 c.Assert(err, IsNil) 2767 chg.AddAll(ts) 2768 2769 s.state.Unlock() 2770 defer s.se.Stop() 2771 s.settle(c) 2772 s.state.Lock() 2773 2774 // ensure all our tasks ran 2775 c.Assert(chg.Err(), IsNil) 2776 c.Assert(chg.IsReady(), Equals, true) 2777 // and both circular snaps got linked 2778 c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{ 2779 op: "link-snap", 2780 path: filepath.Join(dirs.SnapMountDir, "snap-content-plug-compat/42"), 2781 }) 2782 c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{ 2783 op: "link-snap", 2784 path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"), 2785 }) 2786 } 2787 2788 func (s *snapmgrTestSuite) TestInstallDiskSpaceError(c *C) { 2789 restore := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return &osutil.NotEnoughDiskSpaceError{} }) 2790 defer restore() 2791 2792 s.state.Lock() 2793 defer s.state.Unlock() 2794 2795 tr := config.NewTransaction(s.state) 2796 tr.Set("core", "experimental.check-disk-space-install", true) 2797 tr.Commit() 2798 2799 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 2800 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2801 diskSpaceErr := err.(*snapstate.InsufficientSpaceError) 2802 c.Assert(diskSpaceErr, ErrorMatches, `insufficient space in .* to perform "install" change for the following snaps: some-snap`) 2803 c.Check(diskSpaceErr.Path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd")) 2804 c.Check(diskSpaceErr.Snaps, DeepEquals, []string{"some-snap"}) 2805 } 2806 2807 func (s *snapmgrTestSuite) TestInstallSizeError(c *C) { 2808 restore := snapstate.MockInstallSize(func(st *state.State, snaps []*snap.Info, userID int) (uint64, error) { 2809 return 0, fmt.Errorf("boom") 2810 }) 2811 defer restore() 2812 2813 s.state.Lock() 2814 defer s.state.Unlock() 2815 2816 tr := config.NewTransaction(s.state) 2817 tr.Set("core", "experimental.check-disk-space-install", true) 2818 tr.Commit() 2819 2820 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 2821 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2822 c.Check(err, ErrorMatches, `boom`) 2823 } 2824 2825 func (s *snapmgrTestSuite) TestInstallPathWithLayoutsChecksFeatureFlag(c *C) { 2826 s.state.Lock() 2827 defer s.state.Unlock() 2828 2829 // When layouts are disabled we cannot install a local snap depending on the feature. 2830 tr := config.NewTransaction(s.state) 2831 tr.Set("core", "experimental.layouts", false) 2832 tr.Commit() 2833 2834 mockSnap := makeTestSnap(c, `name: some-snap 2835 version: 1.0 2836 layout: 2837 /usr: 2838 bind: $SNAP/usr 2839 `) 2840 _, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{}) 2841 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true") 2842 2843 // When layouts are enabled we can install a local snap depending on the feature. 2844 tr = config.NewTransaction(s.state) 2845 tr.Set("core", "experimental.layouts", true) 2846 tr.Commit() 2847 2848 _, _, err = snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{}) 2849 c.Assert(err, IsNil) 2850 } 2851 2852 func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchKernel(c *C) { 2853 // use the real thing for this one 2854 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 2855 2856 s.state.Lock() 2857 defer s.state.Unlock() 2858 2859 // snapd cannot be installed unless the model uses a base snap 2860 r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18")) 2861 defer r() 2862 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 2863 Sequence: []*snap.SideInfo{ 2864 {RealName: "kernel", Revision: snap.R(11)}, 2865 }, 2866 TrackingChannel: "18/stable", 2867 Current: snap.R(11), 2868 Active: true, 2869 }) 2870 2871 someSnap := makeTestSnap(c, `name: kernel 2872 version: 1.0`) 2873 si := &snap.SideInfo{ 2874 RealName: "kernel", 2875 SnapID: "kernel-id", 2876 Revision: snap.R(42), 2877 Channel: "some-channel", 2878 } 2879 _, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true}) 2880 c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "some-channel"`) 2881 } 2882 2883 func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchGadget(c *C) { 2884 // use the real thing for this one 2885 snapstate.MockOpenSnapFile(backend.OpenSnapFile) 2886 2887 s.state.Lock() 2888 defer s.state.Unlock() 2889 2890 // snapd cannot be installed unless the model uses a base snap 2891 r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18")) 2892 defer r() 2893 snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{ 2894 Sequence: []*snap.SideInfo{ 2895 {RealName: "brand-gadget", Revision: snap.R(11)}, 2896 }, 2897 TrackingChannel: "18/stable", 2898 Current: snap.R(11), 2899 Active: true, 2900 }) 2901 2902 someSnap := makeTestSnap(c, `name: brand-gadget 2903 version: 1.0`) 2904 si := &snap.SideInfo{ 2905 RealName: "brand-gadget", 2906 SnapID: "brand-gadget-id", 2907 Revision: snap.R(42), 2908 Channel: "some-channel", 2909 } 2910 _, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true}) 2911 c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "some-channel"`) 2912 } 2913 2914 func (s *snapmgrTestSuite) TestInstallLayoutsChecksFeatureFlag(c *C) { 2915 s.state.Lock() 2916 defer s.state.Unlock() 2917 2918 // Layouts are now enabled by default. 2919 opts := &snapstate.RevisionOptions{Channel: "channel-for-layout"} 2920 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2921 c.Assert(err, IsNil) 2922 2923 // Layouts can be explicitly disabled. 2924 tr := config.NewTransaction(s.state) 2925 tr.Set("core", "experimental.layouts", false) 2926 tr.Commit() 2927 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2928 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true") 2929 2930 // Layouts can be explicitly enabled. 2931 tr = config.NewTransaction(s.state) 2932 tr.Set("core", "experimental.layouts", true) 2933 tr.Commit() 2934 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2935 c.Assert(err, IsNil) 2936 2937 // The default empty value now means "enabled". 2938 tr = config.NewTransaction(s.state) 2939 tr.Set("core", "experimental.layouts", "") 2940 tr.Commit() 2941 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2942 c.Assert(err, IsNil) 2943 2944 // Layouts are enabled when the controlling flag is reset to nil. 2945 tr = config.NewTransaction(s.state) 2946 tr.Set("core", "experimental.layouts", nil) 2947 tr.Commit() 2948 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2949 c.Assert(err, IsNil) 2950 } 2951 2952 func (s *snapmgrTestSuite) TestInstallUserDaemonsChecksFeatureFlag(c *C) { 2953 if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" { 2954 c.Skip("Ubuntu 14.04 does not support user daemons") 2955 } 2956 2957 s.state.Lock() 2958 defer s.state.Unlock() 2959 2960 // User daemons are disabled by default. 2961 opts := &snapstate.RevisionOptions{Channel: "channel-for-user-daemon"} 2962 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2963 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.user-daemons' to true") 2964 2965 // User daemons can be explicitly enabled. 2966 tr := config.NewTransaction(s.state) 2967 tr.Set("core", "experimental.user-daemons", true) 2968 tr.Commit() 2969 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2970 c.Assert(err, IsNil) 2971 2972 // User daemons can be explicitly disabled. 2973 tr = config.NewTransaction(s.state) 2974 tr.Set("core", "experimental.user-daemons", false) 2975 tr.Commit() 2976 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2977 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.user-daemons' to true") 2978 2979 // The default empty value means "disabled"". 2980 tr = config.NewTransaction(s.state) 2981 tr.Set("core", "experimental.user-daemons", "") 2982 tr.Commit() 2983 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2984 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.user-daemons' to true") 2985 2986 // User daemons are disabled when the controlling flag is reset to nil. 2987 tr = config.NewTransaction(s.state) 2988 tr.Set("core", "experimental.user-daemons", nil) 2989 tr.Commit() 2990 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 2991 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.user-daemons' to true") 2992 } 2993 2994 func (s *snapmgrTestSuite) TestInstallUserDaemonsUsupportedOnTrusty(c *C) { 2995 restore := release.MockReleaseInfo(&release.OS{ID: "ubuntu", VersionID: "14.04"}) 2996 defer restore() 2997 s.state.Lock() 2998 defer s.state.Unlock() 2999 3000 tr := config.NewTransaction(s.state) 3001 tr.Set("core", "experimental.user-daemons", true) 3002 tr.Commit() 3003 3004 // Even with the experimental.user-daemons flag set, user 3005 // daemons are not supported on Trusty 3006 opts := &snapstate.RevisionOptions{Channel: "channel-for-user-daemon"} 3007 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3008 c.Assert(err, ErrorMatches, "user session daemons are not supported on this release") 3009 } 3010 3011 func (s *snapmgrTestSuite) TestInstallDbusActivationChecksFeatureFlag(c *C) { 3012 s.state.Lock() 3013 defer s.state.Unlock() 3014 3015 // D-Bus activation is disabled by default. 3016 opts := &snapstate.RevisionOptions{Channel: "channel-for-dbus-activation"} 3017 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3018 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.dbus-activation' to true") 3019 3020 // D-Bus activation can be explicitly enabled. 3021 tr := config.NewTransaction(s.state) 3022 tr.Set("core", "experimental.dbus-activation", true) 3023 tr.Commit() 3024 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3025 c.Assert(err, IsNil) 3026 3027 // D-Bus activation can be explicitly disabled. 3028 tr = config.NewTransaction(s.state) 3029 tr.Set("core", "experimental.dbus-activation", false) 3030 tr.Commit() 3031 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3032 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.dbus-activation' to true") 3033 3034 // The default empty value means "disabled" 3035 tr = config.NewTransaction(s.state) 3036 tr.Set("core", "experimental.dbus-activation", "") 3037 tr.Commit() 3038 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3039 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.dbus-activation' to true") 3040 3041 // D-Bus activation is disabled when the controlling flag is reset to nil. 3042 tr = config.NewTransaction(s.state) 3043 tr.Set("core", "experimental.dbus-activation", nil) 3044 tr.Commit() 3045 _, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3046 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.dbus-activation' to true") 3047 } 3048 3049 func (s *snapmgrTestSuite) TestInstallValidatesInstanceNames(c *C) { 3050 s.state.Lock() 3051 defer s.state.Unlock() 3052 3053 _, err := snapstate.Install(context.Background(), s.state, "foo--invalid", nil, 0, snapstate.Flags{}) 3054 c.Assert(err, ErrorMatches, `invalid instance name: invalid snap name: "foo--invalid"`) 3055 3056 _, err = snapstate.Install(context.Background(), s.state, "foo_123_456", nil, 0, snapstate.Flags{}) 3057 c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`) 3058 3059 _, _, err = snapstate.InstallMany(s.state, []string{"foo--invalid"}, 0) 3060 c.Assert(err, ErrorMatches, `invalid instance name: invalid snap name: "foo--invalid"`) 3061 3062 _, _, err = snapstate.InstallMany(s.state, []string{"foo_123_456"}, 0) 3063 c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`) 3064 3065 mockSnap := makeTestSnap(c, `name: some-snap 3066 version: 1.0 3067 epoch: 1* 3068 `) 3069 si := snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)} 3070 _, _, err = snapstate.InstallPath(s.state, &si, mockSnap, "some-snap_123_456", "", snapstate.Flags{}) 3071 c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`) 3072 } 3073 3074 func (s *snapmgrTestSuite) TestInstallFailsWhenClassicSnapsAreNotSupported(c *C) { 3075 s.state.Lock() 3076 defer s.state.Unlock() 3077 3078 reset := release.MockReleaseInfo(&release.OS{ 3079 ID: "fedora", 3080 }) 3081 defer reset() 3082 3083 // this needs doing because dirs depends on the release info 3084 dirs.SetRootDir(dirs.GlobalRootDir) 3085 3086 opts := &snapstate.RevisionOptions{Channel: "channel-for-classic"} 3087 _, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true}) 3088 c.Assert(err, ErrorMatches, "classic confinement requires snaps under /snap or symlink from /snap to "+dirs.SnapMountDir) 3089 } 3090 3091 func (s *snapmgrTestSuite) TestInstallUndoRunThroughUndoContextOptional(c *C) { 3092 s.state.Lock() 3093 defer s.state.Unlock() 3094 3095 chg := s.state.NewChange("install", "install a snap") 3096 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 3097 ts, err := snapstate.Install(context.Background(), s.state, "some-snap-no-install-record", opts, s.user.ID, snapstate.Flags{}) 3098 c.Assert(err, IsNil) 3099 chg.AddAll(ts) 3100 3101 tasks := ts.Tasks() 3102 last := tasks[len(tasks)-1] 3103 // sanity 3104 c.Assert(last.Lanes(), HasLen, 1) 3105 terr := s.state.NewTask("error-trigger", "provoking total undo") 3106 terr.WaitFor(last) 3107 terr.JoinLane(last.Lanes()[0]) 3108 chg.AddTask(terr) 3109 3110 s.state.Unlock() 3111 defer s.se.Stop() 3112 s.settle(c) 3113 s.state.Lock() 3114 3115 mountTask := tasks[len(tasks)-11] 3116 c.Assert(mountTask.Kind(), Equals, "mount-snap") 3117 var installRecord backend.InstallRecord 3118 c.Assert(mountTask.Get("install-record", &installRecord), Equals, state.ErrNoState) 3119 } 3120 3121 func (s *snapmgrTestSuite) TestInstallDefaultProviderCircular(c *C) { 3122 s.state.Lock() 3123 defer s.state.Unlock() 3124 3125 snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state}) 3126 3127 repo := interfaces.NewRepository() 3128 ifacerepo.Replace(s.state, repo) 3129 3130 chg := s.state.NewChange("install", "install a snap") 3131 opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)} 3132 ts, err := snapstate.Install(context.Background(), s.state, "snap-content-circular1", opts, s.user.ID, snapstate.Flags{}) 3133 c.Assert(err, IsNil) 3134 chg.AddAll(ts) 3135 3136 s.state.Unlock() 3137 defer s.se.Stop() 3138 s.settle(c) 3139 s.state.Lock() 3140 3141 // ensure all our tasks ran 3142 c.Assert(chg.Err(), IsNil) 3143 c.Assert(chg.IsReady(), Equals, true) 3144 // and both circular snaps got linked 3145 c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{ 3146 op: "link-snap", 3147 path: filepath.Join(dirs.SnapMountDir, "snap-content-circular1/42"), 3148 }) 3149 c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{ 3150 op: "link-snap", 3151 path: filepath.Join(dirs.SnapMountDir, "snap-content-circular2/11"), 3152 }) 3153 } 3154 3155 func (s *snapmgrTestSuite) TestParallelInstallInstallPathExperimentalSwitch(c *C) { 3156 s.state.Lock() 3157 defer s.state.Unlock() 3158 3159 mockSnap := makeTestSnap(c, `name: some-snap 3160 version: 1.0 3161 `) 3162 si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)} 3163 _, _, err := snapstate.InstallPath(s.state, si, mockSnap, "some-snap_foo", "", snapstate.Flags{}) 3164 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.parallel-instances' to true") 3165 3166 // enable parallel instances 3167 tr := config.NewTransaction(s.state) 3168 tr.Set("core", "experimental.parallel-instances", true) 3169 tr.Commit() 3170 3171 _, _, err = snapstate.InstallPath(s.state, si, mockSnap, "some-snap_foo", "", snapstate.Flags{}) 3172 c.Assert(err, IsNil) 3173 } 3174 3175 func (s *snapmgrTestSuite) TestInstallMany(c *C) { 3176 s.state.Lock() 3177 defer s.state.Unlock() 3178 3179 installed, tts, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0) 3180 c.Assert(err, IsNil) 3181 c.Assert(tts, HasLen, 2) 3182 c.Check(installed, DeepEquals, []string{"one", "two"}) 3183 3184 c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true) 3185 3186 for i, ts := range tts { 3187 verifyInstallTasks(c, 0, 0, ts, s.state) 3188 // check that tasksets are in separate lanes 3189 for _, t := range ts.Tasks() { 3190 c.Assert(t.Lanes(), DeepEquals, []int{i + 1}) 3191 } 3192 } 3193 } 3194 3195 func (s *snapmgrTestSuite) TestInstallManyDiskSpaceError(c *C) { 3196 restore := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return &osutil.NotEnoughDiskSpaceError{} }) 3197 defer restore() 3198 3199 s.state.Lock() 3200 defer s.state.Unlock() 3201 3202 tr := config.NewTransaction(s.state) 3203 tr.Set("core", "experimental.check-disk-space-install", true) 3204 tr.Commit() 3205 3206 _, _, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0) 3207 diskSpaceErr := err.(*snapstate.InsufficientSpaceError) 3208 c.Assert(diskSpaceErr, ErrorMatches, `insufficient space in .* to perform "install" change for the following snaps: one, two`) 3209 c.Check(diskSpaceErr.Path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd")) 3210 c.Check(diskSpaceErr.Snaps, DeepEquals, []string{"one", "two"}) 3211 c.Check(diskSpaceErr.ChangeKind, Equals, "install") 3212 } 3213 3214 func (s *snapmgrTestSuite) TestInstallManyDiskCheckDisabled(c *C) { 3215 restore := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return &osutil.NotEnoughDiskSpaceError{} }) 3216 defer restore() 3217 3218 s.state.Lock() 3219 defer s.state.Unlock() 3220 3221 tr := config.NewTransaction(s.state) 3222 tr.Set("core", "experimental.check-disk-space-install", false) 3223 tr.Commit() 3224 3225 _, _, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0) 3226 c.Check(err, IsNil) 3227 } 3228 3229 func (s *snapmgrTestSuite) TestInstallManyTooEarly(c *C) { 3230 s.state.Lock() 3231 defer s.state.Unlock() 3232 3233 s.state.Set("seeded", nil) 3234 3235 _, _, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0) 3236 c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{}) 3237 c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`) 3238 } 3239 3240 func (s *snapmgrTestSuite) TestInstallManyChecksPreconditions(c *C) { 3241 s.state.Lock() 3242 defer s.state.Unlock() 3243 3244 _, _, err := snapstate.InstallMany(s.state, []string{"some-snap-now-classic"}, 0) 3245 c.Assert(err, NotNil) 3246 c.Check(err, DeepEquals, &snapstate.SnapNeedsClassicError{Snap: "some-snap-now-classic"}) 3247 3248 _, _, err = snapstate.InstallMany(s.state, []string{"some-snap_foo"}, 0) 3249 c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.parallel-instances' to true") 3250 } 3251 3252 func verifyStopReason(c *C, ts *state.TaskSet, reason string) { 3253 tl := tasksWithKind(ts, "stop-snap-services") 3254 c.Check(tl, HasLen, 1) 3255 3256 var stopReason string 3257 err := tl[0].Get("stop-reason", &stopReason) 3258 c.Assert(err, IsNil) 3259 c.Check(stopReason, Equals, reason) 3260 3261 } 3262 3263 func (s *snapmgrTestSuite) TestUndoMountSnapFailsInCopyData(c *C) { 3264 s.state.Lock() 3265 defer s.state.Unlock() 3266 3267 chg := s.state.NewChange("install", "install a snap") 3268 opts := &snapstate.RevisionOptions{Channel: "some-channel"} 3269 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3270 c.Assert(err, IsNil) 3271 chg.AddAll(ts) 3272 3273 s.fakeBackend.copySnapDataFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11") 3274 3275 s.state.Unlock() 3276 defer s.se.Stop() 3277 s.settle(c) 3278 s.state.Lock() 3279 3280 expected := fakeOps{ 3281 { 3282 op: "storesvc-snap-action", 3283 userID: 1, 3284 }, 3285 { 3286 op: "storesvc-snap-action:action", 3287 action: store.SnapAction{ 3288 Action: "install", 3289 InstanceName: "some-snap", 3290 Channel: "some-channel", 3291 }, 3292 revno: snap.R(11), 3293 userID: 1, 3294 }, 3295 { 3296 op: "storesvc-download", 3297 name: "some-snap", 3298 }, 3299 { 3300 op: "validate-snap:Doing", 3301 name: "some-snap", 3302 revno: snap.R(11), 3303 }, 3304 { 3305 op: "current", 3306 old: "<no-current>", 3307 }, 3308 { 3309 op: "open-snap-file", 3310 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 3311 sinfo: snap.SideInfo{ 3312 RealName: "some-snap", 3313 SnapID: "some-snap-id", 3314 Channel: "some-channel", 3315 Revision: snap.R(11), 3316 }, 3317 }, 3318 { 3319 op: "setup-snap", 3320 name: "some-snap", 3321 path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"), 3322 revno: snap.R(11), 3323 }, 3324 { 3325 op: "copy-data.failed", 3326 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 3327 old: "<no-old>", 3328 }, 3329 { 3330 op: "remove-snap-data-dir", 3331 name: "some-snap", 3332 path: filepath.Join(dirs.SnapDataDir, "some-snap"), 3333 }, 3334 { 3335 op: "undo-setup-snap", 3336 name: "some-snap", 3337 path: filepath.Join(dirs.SnapMountDir, "some-snap/11"), 3338 stype: "app", 3339 }, 3340 { 3341 op: "remove-snap-dir", 3342 name: "some-snap", 3343 path: filepath.Join(dirs.SnapMountDir, "some-snap"), 3344 }, 3345 } 3346 // start with an easier-to-read error if this fails: 3347 c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 3348 c.Assert(s.fakeBackend.ops, DeepEquals, expected) 3349 } 3350 3351 func (s *snapmgrTestSuite) TestSideInfoPaid(c *C) { 3352 s.state.Lock() 3353 defer s.state.Unlock() 3354 opts := &snapstate.RevisionOptions{Channel: "channel-for-paid"} 3355 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3356 c.Assert(err, IsNil) 3357 3358 chg := s.state.NewChange("install", "install paid snap") 3359 chg.AddAll(ts) 3360 3361 s.state.Unlock() 3362 defer s.se.Stop() 3363 s.settle(c) 3364 s.state.Lock() 3365 3366 // verify snap has paid sideinfo 3367 var snapst snapstate.SnapState 3368 err = snapstate.Get(s.state, "some-snap", &snapst) 3369 c.Assert(err, IsNil) 3370 c.Check(snapst.CurrentSideInfo().Paid, Equals, true) 3371 c.Check(snapst.CurrentSideInfo().Private, Equals, false) 3372 } 3373 3374 func (s *snapmgrTestSuite) TestSideInfoPrivate(c *C) { 3375 s.state.Lock() 3376 defer s.state.Unlock() 3377 opts := &snapstate.RevisionOptions{Channel: "channel-for-private"} 3378 ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{}) 3379 c.Assert(err, IsNil) 3380 3381 chg := s.state.NewChange("install", "install private snap") 3382 chg.AddAll(ts) 3383 3384 s.state.Unlock() 3385 defer s.se.Stop() 3386 s.settle(c) 3387 s.state.Lock() 3388 3389 // verify snap has private sideinfo 3390 var snapst snapstate.SnapState 3391 err = snapstate.Get(s.state, "some-snap", &snapst) 3392 c.Assert(err, IsNil) 3393 c.Check(snapst.CurrentSideInfo().Private, Equals, true) 3394 c.Check(snapst.CurrentSideInfo().Paid, Equals, false) 3395 } 3396 3397 func (s *snapmgrTestSuite) TestGadgetDefaultsInstalled(c *C) { 3398 makeInstalledMockCoreSnap(c) 3399 3400 // using MockSnap, we want to read the bits on disk 3401 snapstate.MockSnapReadInfo(snap.ReadInfo) 3402 3403 s.state.Lock() 3404 defer s.state.Unlock() 3405 3406 s.prepareGadget(c) 3407 3408 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 3409 Active: true, 3410 Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}}, 3411 Current: snap.R(1), 3412 SnapType: "app", 3413 }) 3414 3415 snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0") 3416 3417 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, snapPath, "", "edge", snapstate.Flags{}) 3418 c.Assert(err, IsNil) 3419 3420 var m map[string]interface{} 3421 runHooks := tasksWithKind(ts, "run-hook") 3422 3423 c.Assert(runHooks[0].Kind(), Equals, "run-hook") 3424 err = runHooks[0].Get("hook-context", &m) 3425 c.Assert(err, Equals, state.ErrNoState) 3426 } 3427 3428 func makeInstalledMockCoreSnap(c *C) { 3429 coreSnapYaml := `name: core 3430 version: 1.0 3431 type: os 3432 ` 3433 snaptest.MockSnap(c, coreSnapYaml, &snap.SideInfo{ 3434 RealName: "core", 3435 Revision: snap.R(1), 3436 }) 3437 } 3438 3439 func (s *snapmgrTestSuite) TestGadgetDefaults(c *C) { 3440 r := release.MockOnClassic(false) 3441 defer r() 3442 3443 makeInstalledMockCoreSnap(c) 3444 3445 // using MockSnap, we want to read the bits on disk 3446 snapstate.MockSnapReadInfo(snap.ReadInfo) 3447 3448 s.state.Lock() 3449 defer s.state.Unlock() 3450 3451 s.prepareGadget(c) 3452 3453 snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0") 3454 3455 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{}) 3456 c.Assert(err, IsNil) 3457 3458 var m map[string]interface{} 3459 runHooks := tasksWithKind(ts, "run-hook") 3460 3461 c.Assert(taskKinds(runHooks), DeepEquals, []string{ 3462 "run-hook[install]", 3463 "run-hook[configure]", 3464 "run-hook[check-health]", 3465 }) 3466 err = runHooks[1].Get("hook-context", &m) 3467 c.Assert(err, IsNil) 3468 c.Assert(m, DeepEquals, map[string]interface{}{"use-defaults": true}) 3469 } 3470 3471 func (s *snapmgrTestSuite) TestGadgetDefaultsNotForOS(c *C) { 3472 r := release.MockOnClassic(false) 3473 defer r() 3474 3475 // using MockSnap, we want to read the bits on disk 3476 snapstate.MockSnapReadInfo(snap.ReadInfo) 3477 3478 s.state.Lock() 3479 defer s.state.Unlock() 3480 3481 snapstate.Set(s.state, "core", nil) 3482 3483 s.prepareGadget(c) 3484 3485 const coreSnapYaml = ` 3486 name: core 3487 type: os 3488 version: 1.0 3489 ` 3490 snapPath := makeTestSnap(c, coreSnapYaml) 3491 3492 ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "core", SnapID: "core-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{}) 3493 c.Assert(err, IsNil) 3494 3495 var m map[string]interface{} 3496 runHooks := tasksWithKind(ts, "run-hook") 3497 3498 c.Assert(taskKinds(runHooks), DeepEquals, []string{ 3499 "run-hook[install]", 3500 "run-hook[configure]", 3501 "run-hook[check-health]", 3502 }) 3503 // use-defaults flag is part of hook-context which isn't set 3504 err = runHooks[1].Get("hook-context", &m) 3505 c.Assert(err, Equals, state.ErrNoState) 3506 } 3507 3508 func (s *snapmgrTestSuite) TestGadgetDefaultsAreNormalizedForConfigHook(c *C) { 3509 var mockGadgetSnapYaml = ` 3510 name: canonical-pc 3511 type: gadget 3512 ` 3513 var mockGadgetYaml = []byte(` 3514 defaults: 3515 otheridididididididididididididi: 3516 foo: 3517 bar: baz 3518 num: 1.305 3519 3520 volumes: 3521 volume-id: 3522 bootloader: grub 3523 `) 3524 3525 info := snaptest.MockSnap(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(2)}) 3526 err := ioutil.WriteFile(filepath.Join(info.MountDir(), "meta", "gadget.yaml"), mockGadgetYaml, 0644) 3527 c.Assert(err, IsNil) 3528 3529 gi, err := gadget.ReadInfo(info.MountDir(), nil) 3530 c.Assert(err, IsNil) 3531 c.Assert(gi, NotNil) 3532 3533 snapName := "some-snap" 3534 hooksup := &hookstate.HookSetup{ 3535 Snap: snapName, 3536 Hook: "configure", 3537 Optional: true, 3538 IgnoreError: false, 3539 TrackError: false, 3540 } 3541 3542 var contextData map[string]interface{} 3543 contextData = map[string]interface{}{"patch": gi.Defaults} 3544 3545 s.state.Lock() 3546 defer s.state.Unlock() 3547 c.Assert(hookstate.HookTask(s.state, "", hooksup, contextData), NotNil) 3548 }