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