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