github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/snapstate/handlers_mount_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 "path/filepath" 24 "time" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/dirs" 29 "github.com/snapcore/snapd/osutil" 30 "github.com/snapcore/snapd/overlord/snapstate" 31 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 32 "github.com/snapcore/snapd/snap" 33 "github.com/snapcore/snapd/snap/snaptest" 34 ) 35 36 type mountSnapSuite struct { 37 baseHandlerSuite 38 } 39 40 var _ = Suite(&mountSnapSuite{}) 41 42 func (s *mountSnapSuite) SetUpTest(c *C) { 43 s.setup(c, nil) 44 s.AddCleanup(snapstatetest.MockDeviceModel(DefaultModel())) 45 } 46 47 func (s *mountSnapSuite) TestDoMountSnapDoesNotRemovesSnaps(c *C) { 48 v1 := "name: mock\nversion: 1.0\n" 49 testSnap := snaptest.MakeTestSnapWithFiles(c, v1, nil) 50 51 s.state.Lock() 52 53 t := s.state.NewTask("mount-snap", "test") 54 t.Set("snap-setup", &snapstate.SnapSetup{ 55 SideInfo: &snap.SideInfo{ 56 RealName: "foo", 57 Revision: snap.R(33), 58 }, 59 SnapPath: testSnap, 60 DownloadInfo: &snap.DownloadInfo{DownloadURL: "https://some"}, 61 }) 62 s.state.NewChange("dummy", "...").AddTask(t) 63 64 s.state.Unlock() 65 66 s.se.Ensure() 67 s.se.Wait() 68 69 c.Assert(osutil.FileExists(testSnap), Equals, true) 70 } 71 72 func (s *mountSnapSuite) TestDoUndoMountSnap(c *C) { 73 v1 := "name: core\nversion: 1.0\nepoch: 1\n" 74 testSnap := snaptest.MakeTestSnapWithFiles(c, v1, nil) 75 76 s.state.Lock() 77 defer s.state.Unlock() 78 si1 := &snap.SideInfo{ 79 RealName: "core", 80 Revision: snap.R(1), 81 } 82 si2 := &snap.SideInfo{ 83 RealName: "core", 84 Revision: snap.R(2), 85 } 86 snapstate.Set(s.state, "core", &snapstate.SnapState{ 87 Sequence: []*snap.SideInfo{si1}, 88 Current: si1.Revision, 89 SnapType: "os", 90 }) 91 92 t := s.state.NewTask("mount-snap", "test") 93 t.Set("snap-setup", &snapstate.SnapSetup{ 94 SideInfo: si2, 95 SnapPath: testSnap, 96 }) 97 chg := s.state.NewChange("dummy", "...") 98 chg.AddTask(t) 99 100 terr := s.state.NewTask("error-trigger", "provoking total undo") 101 terr.WaitFor(t) 102 chg.AddTask(terr) 103 104 s.state.Unlock() 105 106 for i := 0; i < 3; i++ { 107 s.se.Ensure() 108 s.se.Wait() 109 } 110 111 s.state.Lock() 112 113 // ensure undo was called the right way 114 c.Check(s.fakeBackend.ops, DeepEquals, fakeOps{ 115 { 116 op: "current", 117 old: filepath.Join(dirs.SnapMountDir, "core/1"), 118 }, 119 { 120 op: "setup-snap", 121 name: "core", 122 path: testSnap, 123 revno: snap.R(2), 124 }, 125 { 126 op: "undo-setup-snap", 127 name: "core", 128 path: filepath.Join(dirs.SnapMountDir, "core/2"), 129 stype: "os", 130 }, 131 { 132 op: "remove-snap-dir", 133 name: "core", 134 path: filepath.Join(dirs.SnapMountDir, "core"), 135 }, 136 }) 137 138 } 139 140 func (s *mountSnapSuite) TestDoMountSnapErrorReadInfo(c *C) { 141 v1 := "name: borken\nversion: 1.0\nepoch: 1\n" 142 testSnap := snaptest.MakeTestSnapWithFiles(c, v1, nil) 143 144 s.state.Lock() 145 defer s.state.Unlock() 146 si1 := &snap.SideInfo{ 147 RealName: "borken", 148 Revision: snap.R(1), 149 } 150 si2 := &snap.SideInfo{ 151 RealName: "borken", 152 Revision: snap.R(2), 153 } 154 snapstate.Set(s.state, "borken", &snapstate.SnapState{ 155 Sequence: []*snap.SideInfo{si1}, 156 Current: si1.Revision, 157 SnapType: "app", 158 }) 159 160 t := s.state.NewTask("mount-snap", "test") 161 t.Set("snap-setup", &snapstate.SnapSetup{ 162 SideInfo: si2, 163 SnapPath: testSnap, 164 }) 165 chg := s.state.NewChange("dummy", "...") 166 chg.AddTask(t) 167 168 s.state.Unlock() 169 170 for i := 0; i < 3; i++ { 171 s.se.Ensure() 172 s.se.Wait() 173 } 174 175 s.state.Lock() 176 177 c.Check(chg.Err(), ErrorMatches, `(?s).*cannot read info for "borken" snap.*`) 178 179 c.Check(s.fakeBackend.ops, DeepEquals, fakeOps{ 180 { 181 op: "current", 182 old: filepath.Join(dirs.SnapMountDir, "borken/1"), 183 }, 184 { 185 op: "setup-snap", 186 name: "borken", 187 path: testSnap, 188 revno: snap.R(2), 189 }, 190 { 191 op: "undo-setup-snap", 192 name: "borken", 193 path: filepath.Join(dirs.SnapMountDir, "borken/2"), 194 stype: "app", 195 }, 196 { 197 op: "remove-snap-dir", 198 name: "borken", 199 path: filepath.Join(dirs.SnapMountDir, "borken"), 200 }, 201 }) 202 } 203 204 func (s *mountSnapSuite) TestDoMountSnapEpochError(c *C) { 205 v1 := "name: some-snap\nversion: 1.0\nepoch: 13\n" 206 testSnap := snaptest.MakeTestSnapWithFiles(c, v1, nil) 207 208 s.state.Lock() 209 defer s.state.Unlock() 210 si1 := &snap.SideInfo{ 211 RealName: "some-snap", 212 Revision: snap.R(1), 213 } 214 si2 := &snap.SideInfo{ 215 RealName: "some-snap", 216 Revision: snap.R(2), 217 } 218 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 219 Sequence: []*snap.SideInfo{si1}, 220 Current: si1.Revision, 221 SnapType: "app", 222 }) 223 224 t := s.state.NewTask("mount-snap", "test") 225 t.Set("snap-setup", &snapstate.SnapSetup{ 226 SideInfo: si2, 227 SnapPath: testSnap, 228 }) 229 chg := s.state.NewChange("dummy", "...") 230 chg.AddTask(t) 231 232 s.state.Unlock() 233 234 for i := 0; i < 3; i++ { 235 s.se.Ensure() 236 s.se.Wait() 237 } 238 239 s.state.Lock() 240 241 c.Check(chg.Err(), ErrorMatches, `(?s).* new revision 2 with epoch .* can't read the current epoch of [^ ]*`) 242 c.Check(s.fakeBackend.ops, DeepEquals, fakeOps{ 243 { 244 op: "current", 245 old: filepath.Join(dirs.SnapMountDir, "some-snap/1"), 246 }, 247 }) 248 } 249 250 func (s *mountSnapSuite) TestDoMountSnapErrorSetupSnap(c *C) { 251 v1 := "name: borken\nversion: 1.0\n" 252 testSnap := snaptest.MakeTestSnapWithFiles(c, v1, nil) 253 254 s.state.Lock() 255 defer s.state.Unlock() 256 si := &snap.SideInfo{ 257 RealName: "borken-in-setup", 258 Revision: snap.R(2), 259 } 260 261 t := s.state.NewTask("mount-snap", "test") 262 t.Set("snap-setup", &snapstate.SnapSetup{ 263 SideInfo: si, 264 SnapPath: testSnap, 265 }) 266 chg := s.state.NewChange("dummy", "...") 267 chg.AddTask(t) 268 269 s.state.Unlock() 270 271 for i := 0; i < 3; i++ { 272 s.se.Ensure() 273 s.se.Wait() 274 } 275 276 s.state.Lock() 277 278 c.Check(chg.Err(), ErrorMatches, `(?s).*cannot install snap "borken-in-setup".*`) 279 280 c.Check(s.fakeBackend.ops, DeepEquals, fakeOps{ 281 { 282 op: "current", 283 old: "<no-current>", 284 }, 285 { 286 op: "setup-snap", 287 name: "borken-in-setup", 288 path: testSnap, 289 revno: snap.R(2), 290 }, 291 { 292 op: "remove-snap-dir", 293 name: "borken-in-setup", 294 path: filepath.Join(dirs.SnapMountDir, "borken-in-setup"), 295 }, 296 }) 297 } 298 299 func (s *mountSnapSuite) TestDoMountSnapUndoError(c *C) { 300 v1 := "name: borken-undo-setup\nversion: 1.0\nepoch: 1\n" 301 testSnap := snaptest.MakeTestSnapWithFiles(c, v1, nil) 302 303 s.state.Lock() 304 defer s.state.Unlock() 305 si1 := &snap.SideInfo{ 306 RealName: "borken-undo-setup", 307 Revision: snap.R(1), 308 } 309 si2 := &snap.SideInfo{ 310 RealName: "borken-undo-setup", 311 Revision: snap.R(2), 312 } 313 snapstate.Set(s.state, "borken-undo-setup", &snapstate.SnapState{ 314 Sequence: []*snap.SideInfo{si1}, 315 Current: si1.Revision, 316 SnapType: "app", 317 }) 318 319 t := s.state.NewTask("mount-snap", "test") 320 t.Set("snap-setup", &snapstate.SnapSetup{ 321 SideInfo: si2, 322 SnapPath: testSnap, 323 }) 324 chg := s.state.NewChange("dummy", "...") 325 chg.AddTask(t) 326 327 s.state.Unlock() 328 329 for i := 0; i < 3; i++ { 330 s.se.Ensure() 331 s.se.Wait() 332 } 333 334 s.state.Lock() 335 336 c.Check(chg.Err(), ErrorMatches, `(?s).*cannot undo partial setup snap "borken-undo-setup": cannot undo setup of "borken-undo-setup" snap.*cannot read info for "borken-undo-setup" snap.*`) 337 338 c.Check(s.fakeBackend.ops, DeepEquals, fakeOps{ 339 { 340 op: "current", 341 old: filepath.Join(dirs.SnapMountDir, "borken-undo-setup/1"), 342 }, 343 { 344 op: "setup-snap", 345 name: "borken-undo-setup", 346 path: testSnap, 347 revno: snap.R(2), 348 }, 349 { 350 op: "undo-setup-snap", 351 name: "borken-undo-setup", 352 path: filepath.Join(dirs.SnapMountDir, "borken-undo-setup/2"), 353 stype: "app", 354 }, 355 { 356 op: "remove-snap-dir", 357 name: "borken-undo-setup", 358 path: filepath.Join(dirs.SnapMountDir, "borken-undo-setup"), 359 }, 360 }) 361 } 362 363 func (s *mountSnapSuite) TestDoMountSnapErrorNotFound(c *C) { 364 r := snapstate.MockMountPollInterval(10 * time.Millisecond) 365 defer r() 366 367 v1 := "name: not-there\nversion: 1.0\nepoch: 1\n" 368 testSnap := snaptest.MakeTestSnapWithFiles(c, v1, nil) 369 370 s.state.Lock() 371 defer s.state.Unlock() 372 si1 := &snap.SideInfo{ 373 RealName: "not-there", 374 Revision: snap.R(1), 375 } 376 si2 := &snap.SideInfo{ 377 RealName: "not-there", 378 Revision: snap.R(2), 379 } 380 snapstate.Set(s.state, "not-there", &snapstate.SnapState{ 381 Sequence: []*snap.SideInfo{si1}, 382 Current: si1.Revision, 383 SnapType: "app", 384 }) 385 386 t := s.state.NewTask("mount-snap", "test") 387 t.Set("snap-setup", &snapstate.SnapSetup{ 388 SideInfo: si2, 389 SnapPath: testSnap, 390 }) 391 chg := s.state.NewChange("dummy", "...") 392 chg.AddTask(t) 393 394 s.state.Unlock() 395 396 for i := 0; i < 3; i++ { 397 s.se.Ensure() 398 s.se.Wait() 399 } 400 401 s.state.Lock() 402 403 c.Check(chg.Err(), ErrorMatches, `(?s).*cannot proceed, expected snap "not-there" revision 2 to be mounted but is not.*`) 404 405 c.Check(s.fakeBackend.ops, DeepEquals, fakeOps{ 406 { 407 op: "current", 408 old: filepath.Join(dirs.SnapMountDir, "not-there/1"), 409 }, 410 { 411 op: "setup-snap", 412 name: "not-there", 413 path: testSnap, 414 revno: snap.R(2), 415 }, 416 { 417 op: "undo-setup-snap", 418 name: "not-there", 419 path: filepath.Join(dirs.SnapMountDir, "not-there/2"), 420 stype: "app", 421 }, 422 { 423 op: "remove-snap-dir", 424 name: "not-there", 425 path: filepath.Join(dirs.SnapMountDir, "not-there"), 426 }, 427 }) 428 } 429 430 func (s *mountSnapSuite) TestDoMountNotMountedRetryRetry(c *C) { 431 r := snapstate.MockMountPollInterval(10 * time.Millisecond) 432 defer r() 433 n := 0 434 slowMountedReadInfo := func(name string, si *snap.SideInfo) (*snap.Info, error) { 435 n++ 436 if n < 3 { 437 return nil, &snap.NotFoundError{Snap: "not-there", Revision: si.Revision} 438 } 439 return &snap.Info{ 440 SideInfo: *si, 441 }, nil 442 } 443 444 r1 := snapstate.MockSnapReadInfo(slowMountedReadInfo) 445 defer r1() 446 447 v1 := "name: not-there\nversion: 1.0\n" 448 testSnap := snaptest.MakeTestSnapWithFiles(c, v1, nil) 449 450 s.state.Lock() 451 defer s.state.Unlock() 452 si1 := &snap.SideInfo{ 453 RealName: "not-there", 454 Revision: snap.R(1), 455 } 456 si2 := &snap.SideInfo{ 457 RealName: "not-there", 458 Revision: snap.R(2), 459 } 460 snapstate.Set(s.state, "not-there", &snapstate.SnapState{ 461 Sequence: []*snap.SideInfo{si1}, 462 Current: si1.Revision, 463 SnapType: "app", 464 }) 465 466 t := s.state.NewTask("mount-snap", "test") 467 t.Set("snap-setup", &snapstate.SnapSetup{ 468 SideInfo: si2, 469 SnapPath: testSnap, 470 }) 471 chg := s.state.NewChange("dummy", "...") 472 chg.AddTask(t) 473 474 s.state.Unlock() 475 476 for i := 0; i < 3; i++ { 477 s.se.Ensure() 478 s.se.Wait() 479 } 480 481 s.state.Lock() 482 483 c.Check(chg.IsReady(), Equals, true) 484 c.Check(chg.Err(), IsNil) 485 486 c.Check(s.fakeBackend.ops, DeepEquals, fakeOps{ 487 { 488 op: "current", 489 old: filepath.Join(dirs.SnapMountDir, "not-there/1"), 490 }, 491 { 492 op: "setup-snap", 493 name: "not-there", 494 path: testSnap, 495 revno: snap.R(2), 496 }, 497 }) 498 }