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