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