github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapshotstate/backend/backend_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 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 backend_test 21 22 import ( 23 "archive/zip" 24 "bytes" 25 "context" 26 "errors" 27 "fmt" 28 "io" 29 "io/ioutil" 30 "os" 31 "os/exec" 32 "os/user" 33 "path/filepath" 34 "sort" 35 "strings" 36 "testing" 37 38 "gopkg.in/check.v1" 39 40 "github.com/snapcore/snapd/client" 41 "github.com/snapcore/snapd/dirs" 42 "github.com/snapcore/snapd/logger" 43 "github.com/snapcore/snapd/osutil/sys" 44 "github.com/snapcore/snapd/overlord/snapshotstate/backend" 45 "github.com/snapcore/snapd/snap" 46 ) 47 48 type snapshotSuite struct { 49 root string 50 restore []func() 51 tarPath string 52 isTesting bool 53 } 54 55 // silly wrappers to get better failure messages 56 type isTestingSuite struct{ snapshotSuite } 57 type noTestingSuite struct{ snapshotSuite } 58 59 var _ = check.Suite(&isTestingSuite{snapshotSuite{isTesting: true}}) 60 var _ = check.Suite(&noTestingSuite{snapshotSuite{isTesting: false}}) 61 62 // tie gocheck into testing 63 func TestSnapshot(t *testing.T) { check.TestingT(t) } 64 65 type tableT struct { 66 dir string 67 name string 68 content string 69 } 70 71 func table(si snap.PlaceInfo, homeDir string) []tableT { 72 return []tableT{ 73 { 74 dir: si.DataDir(), 75 name: "foo", 76 content: "versioned system canary\n", 77 }, { 78 dir: si.CommonDataDir(), 79 name: "bar", 80 content: "common system canary\n", 81 }, { 82 dir: si.UserDataDir(homeDir), 83 name: "ufoo", 84 content: "versioned user canary\n", 85 }, { 86 dir: si.UserCommonDataDir(homeDir), 87 name: "ubar", 88 content: "common user canary\n", 89 }, 90 } 91 } 92 93 func (s *snapshotSuite) SetUpTest(c *check.C) { 94 s.root = c.MkDir() 95 96 dirs.SetRootDir(s.root) 97 98 si := snap.MinimalPlaceInfo("hello-snap", snap.R(42)) 99 100 for _, t := range table(si, filepath.Join(dirs.GlobalRootDir, "home/snapuser")) { 101 c.Check(os.MkdirAll(t.dir, 0755), check.IsNil) 102 c.Check(ioutil.WriteFile(filepath.Join(t.dir, t.name), []byte(t.content), 0644), check.IsNil) 103 } 104 105 cur, err := user.Current() 106 c.Assert(err, check.IsNil) 107 108 s.restore = append(s.restore, backend.MockUserLookup(func(username string) (*user.User, error) { 109 if username != "snapuser" { 110 return nil, user.UnknownUserError(username) 111 } 112 rv := *cur 113 rv.Username = username 114 rv.HomeDir = filepath.Join(dirs.GlobalRootDir, "home/snapuser") 115 return &rv, nil 116 }), 117 backend.MockIsTesting(s.isTesting), 118 ) 119 120 s.tarPath, err = exec.LookPath("tar") 121 c.Assert(err, check.IsNil) 122 } 123 124 func (s *snapshotSuite) TearDownTest(c *check.C) { 125 dirs.SetRootDir("") 126 for _, restore := range s.restore { 127 restore() 128 } 129 } 130 131 func hashkeys(snapshot *client.Snapshot) (keys []string) { 132 for k := range snapshot.SHA3_384 { 133 keys = append(keys, k) 134 } 135 sort.Strings(keys) 136 137 return keys 138 } 139 140 func (s *snapshotSuite) TestIterBailsIfContextDone(c *check.C) { 141 ctx, cancel := context.WithCancel(context.Background()) 142 cancel() 143 triedToOpenDir := false 144 defer backend.MockOsOpen(func(string) (*os.File, error) { 145 triedToOpenDir = true 146 return nil, nil // deal with it 147 })() 148 149 err := backend.Iter(ctx, nil) 150 c.Check(err, check.Equals, context.Canceled) 151 c.Check(triedToOpenDir, check.Equals, false) 152 } 153 154 func (s *snapshotSuite) TestIterBailsIfContextDoneMidway(c *check.C) { 155 ctx, cancel := context.WithCancel(context.Background()) 156 triedToOpenDir := false 157 defer backend.MockOsOpen(func(string) (*os.File, error) { 158 triedToOpenDir = true 159 return os.Open(os.DevNull) 160 })() 161 readNames := 0 162 defer backend.MockDirNames(func(*os.File, int) ([]string, error) { 163 readNames++ 164 cancel() 165 return []string{"hello"}, nil 166 })() 167 triedToOpenSnapshot := false 168 defer backend.MockOpen(func(string) (*backend.Reader, error) { 169 triedToOpenSnapshot = true 170 return nil, nil 171 })() 172 173 err := backend.Iter(ctx, nil) 174 c.Check(err, check.Equals, context.Canceled) 175 c.Check(triedToOpenDir, check.Equals, true) 176 // bails as soon as 177 c.Check(readNames, check.Equals, 1) 178 c.Check(triedToOpenSnapshot, check.Equals, false) 179 } 180 181 func (s *snapshotSuite) TestIterReturnsOkIfSnapshotsDirNonexistent(c *check.C) { 182 triedToOpenDir := false 183 defer backend.MockOsOpen(func(string) (*os.File, error) { 184 triedToOpenDir = true 185 return nil, os.ErrNotExist 186 })() 187 188 err := backend.Iter(context.Background(), nil) 189 c.Check(err, check.IsNil) 190 c.Check(triedToOpenDir, check.Equals, true) 191 } 192 193 func (s *snapshotSuite) TestIterBailsIfSnapshotsDirFails(c *check.C) { 194 triedToOpenDir := false 195 defer backend.MockOsOpen(func(string) (*os.File, error) { 196 triedToOpenDir = true 197 return nil, os.ErrInvalid 198 })() 199 200 err := backend.Iter(context.Background(), nil) 201 c.Check(err, check.ErrorMatches, "cannot open snapshots directory: invalid argument") 202 c.Check(triedToOpenDir, check.Equals, true) 203 } 204 205 func (s *snapshotSuite) TestIterWarnsOnOpenErrorIfSnapshotNil(c *check.C) { 206 logbuf, restore := logger.MockLogger() 207 defer restore() 208 triedToOpenDir := false 209 defer backend.MockOsOpen(func(string) (*os.File, error) { 210 triedToOpenDir = true 211 return new(os.File), nil 212 })() 213 readNames := 0 214 defer backend.MockDirNames(func(*os.File, int) ([]string, error) { 215 readNames++ 216 if readNames > 1 { 217 return nil, io.EOF 218 } 219 return []string{"hello"}, nil 220 })() 221 triedToOpenSnapshot := false 222 defer backend.MockOpen(func(string) (*backend.Reader, error) { 223 triedToOpenSnapshot = true 224 return nil, os.ErrInvalid 225 })() 226 227 calledF := false 228 f := func(snapshot *backend.Reader) error { 229 calledF = true 230 return nil 231 } 232 233 err := backend.Iter(context.Background(), f) 234 // snapshot open errors are not failures: 235 c.Check(err, check.IsNil) 236 c.Check(triedToOpenDir, check.Equals, true) 237 c.Check(readNames, check.Equals, 2) 238 c.Check(triedToOpenSnapshot, check.Equals, true) 239 c.Check(logbuf.String(), check.Matches, `(?m).* Cannot open snapshot "hello": invalid argument.`) 240 c.Check(calledF, check.Equals, false) 241 } 242 243 func (s *snapshotSuite) TestIterCallsFuncIfSnapshotNotNil(c *check.C) { 244 logbuf, restore := logger.MockLogger() 245 defer restore() 246 triedToOpenDir := false 247 defer backend.MockOsOpen(func(string) (*os.File, error) { 248 triedToOpenDir = true 249 return new(os.File), nil 250 })() 251 readNames := 0 252 defer backend.MockDirNames(func(*os.File, int) ([]string, error) { 253 readNames++ 254 if readNames > 1 { 255 return nil, io.EOF 256 } 257 return []string{"hello"}, nil 258 })() 259 triedToOpenSnapshot := false 260 defer backend.MockOpen(func(string) (*backend.Reader, error) { 261 triedToOpenSnapshot = true 262 // NOTE non-nil reader, and error, returned 263 r := backend.Reader{} 264 r.Broken = "xyzzy" 265 return &r, os.ErrInvalid 266 })() 267 268 calledF := false 269 f := func(snapshot *backend.Reader) error { 270 c.Check(snapshot.Broken, check.Equals, "xyzzy") 271 calledF = true 272 return nil 273 } 274 275 err := backend.Iter(context.Background(), f) 276 // snapshot open errors are not failures: 277 c.Check(err, check.IsNil) 278 c.Check(triedToOpenDir, check.Equals, true) 279 c.Check(readNames, check.Equals, 2) 280 c.Check(triedToOpenSnapshot, check.Equals, true) 281 c.Check(logbuf.String(), check.Equals, "") 282 c.Check(calledF, check.Equals, true) 283 } 284 285 func (s *snapshotSuite) TestIterReportsCloseError(c *check.C) { 286 logbuf, restore := logger.MockLogger() 287 defer restore() 288 triedToOpenDir := false 289 defer backend.MockOsOpen(func(string) (*os.File, error) { 290 triedToOpenDir = true 291 return new(os.File), nil 292 })() 293 readNames := 0 294 defer backend.MockDirNames(func(*os.File, int) ([]string, error) { 295 readNames++ 296 if readNames > 1 { 297 return nil, io.EOF 298 } 299 return []string{"hello"}, nil 300 })() 301 triedToOpenSnapshot := false 302 defer backend.MockOpen(func(string) (*backend.Reader, error) { 303 triedToOpenSnapshot = true 304 r := backend.Reader{} 305 r.SetID = 42 306 return &r, nil 307 })() 308 309 calledF := false 310 f := func(snapshot *backend.Reader) error { 311 c.Check(snapshot.SetID, check.Equals, uint64(42)) 312 calledF = true 313 return nil 314 } 315 316 err := backend.Iter(context.Background(), f) 317 // snapshot close errors _are_ failures (because they're completely unexpected): 318 c.Check(err, check.Equals, os.ErrInvalid) 319 c.Check(triedToOpenDir, check.Equals, true) 320 c.Check(readNames, check.Equals, 1) // never gets to read another one 321 c.Check(triedToOpenSnapshot, check.Equals, true) 322 c.Check(logbuf.String(), check.Equals, "") 323 c.Check(calledF, check.Equals, true) 324 } 325 326 func (s *snapshotSuite) TestList(c *check.C) { 327 logbuf, restore := logger.MockLogger() 328 defer restore() 329 defer backend.MockOsOpen(func(string) (*os.File, error) { return new(os.File), nil })() 330 readNames := 0 331 defer backend.MockDirNames(func(*os.File, int) ([]string, error) { 332 readNames++ 333 if readNames > 4 { 334 return nil, io.EOF 335 } 336 return []string{ 337 fmt.Sprintf("%d_foo", readNames), 338 fmt.Sprintf("%d_bar", readNames), 339 fmt.Sprintf("%d_baz", readNames), 340 }, nil 341 })() 342 defer backend.MockOpen(func(fn string) (*backend.Reader, error) { 343 var id uint64 344 var snapname string 345 fn = filepath.Base(fn) 346 _, err := fmt.Sscanf(fn, "%d_%s", &id, &snapname) 347 c.Assert(err, check.IsNil, check.Commentf(fn)) 348 f, err := os.Open(os.DevNull) 349 c.Assert(err, check.IsNil, check.Commentf(fn)) 350 return &backend.Reader{ 351 File: f, 352 Snapshot: client.Snapshot{ 353 SetID: id, 354 Snap: snapname, 355 SnapID: "id-for-" + snapname, 356 Version: "v1.0-" + snapname, 357 Revision: snap.R(int(id)), 358 }, 359 }, nil 360 })() 361 362 type tableT struct { 363 setID uint64 364 snapnames []string 365 numSets int 366 numShots int 367 predicate func(*client.Snapshot) bool 368 } 369 table := []tableT{ 370 {0, nil, 4, 12, nil}, 371 {0, []string{"foo"}, 4, 4, func(snapshot *client.Snapshot) bool { return snapshot.Snap == "foo" }}, 372 {1, nil, 1, 3, func(snapshot *client.Snapshot) bool { return snapshot.SetID == 1 }}, 373 {2, []string{"bar"}, 1, 1, func(snapshot *client.Snapshot) bool { return snapshot.Snap == "bar" && snapshot.SetID == 2 }}, 374 {0, []string{"foo", "bar"}, 4, 8, func(snapshot *client.Snapshot) bool { return snapshot.Snap == "foo" || snapshot.Snap == "bar" }}, 375 } 376 377 for i, t := range table { 378 comm := check.Commentf("%d: %d/%v", i, t.setID, t.snapnames) 379 // reset 380 readNames = 0 381 logbuf.Reset() 382 383 sets, err := backend.List(context.Background(), t.setID, t.snapnames) 384 c.Check(err, check.IsNil, comm) 385 c.Check(readNames, check.Equals, 5, comm) 386 c.Check(logbuf.String(), check.Equals, "", comm) 387 c.Check(sets, check.HasLen, t.numSets, comm) 388 nShots := 0 389 fnTpl := filepath.Join(dirs.SnapshotsDir, "%d_%s_%s_%s.zip") 390 for j, ss := range sets { 391 for k, snapshot := range ss.Snapshots { 392 comm := check.Commentf("%d: %d/%v #%d/%d", i, t.setID, t.snapnames, j, k) 393 if t.predicate != nil { 394 c.Check(t.predicate(snapshot), check.Equals, true, comm) 395 } 396 nShots++ 397 fn := fmt.Sprintf(fnTpl, snapshot.SetID, snapshot.Snap, snapshot.Version, snapshot.Revision) 398 c.Check(backend.Filename(snapshot), check.Equals, fn, comm) 399 c.Check(snapshot.SnapID, check.Equals, "id-for-"+snapshot.Snap) 400 } 401 } 402 c.Check(nShots, check.Equals, t.numShots) 403 } 404 } 405 406 func (s *snapshotSuite) TestAddDirToZipBails(c *check.C) { 407 snapshot := &client.Snapshot{SetID: 42, Snap: "a-snap"} 408 buf, restore := logger.MockLogger() 409 defer restore() 410 // note as the zip is nil this would panic if it didn't bail 411 c.Check(backend.AddDirToZip(nil, snapshot, nil, "", "an/entry", filepath.Join(s.root, "nonexistent")), check.IsNil) 412 // no log for the non-existent case 413 c.Check(buf.String(), check.Equals, "") 414 buf.Reset() 415 c.Check(backend.AddDirToZip(nil, snapshot, nil, "", "an/entry", "/etc/passwd"), check.IsNil) 416 c.Check(buf.String(), check.Matches, "(?m).* is not a directory.") 417 } 418 419 func (s *snapshotSuite) TestAddDirToZipTarFails(c *check.C) { 420 d := filepath.Join(s.root, "foo") 421 c.Assert(os.MkdirAll(filepath.Join(d, "bar"), 0755), check.IsNil) 422 c.Assert(os.MkdirAll(filepath.Join(s.root, "common"), 0755), check.IsNil) 423 424 ctx, cancel := context.WithCancel(context.Background()) 425 cancel() 426 427 var buf bytes.Buffer 428 z := zip.NewWriter(&buf) 429 c.Assert(backend.AddDirToZip(ctx, nil, z, "", "an/entry", d), check.ErrorMatches, ".* context canceled") 430 } 431 432 func (s *snapshotSuite) TestAddDirToZip(c *check.C) { 433 d := filepath.Join(s.root, "foo") 434 c.Assert(os.MkdirAll(filepath.Join(d, "bar"), 0755), check.IsNil) 435 c.Assert(os.MkdirAll(filepath.Join(s.root, "common"), 0755), check.IsNil) 436 c.Assert(ioutil.WriteFile(filepath.Join(d, "bar", "baz"), []byte("hello\n"), 0644), check.IsNil) 437 438 var buf bytes.Buffer 439 z := zip.NewWriter(&buf) 440 snapshot := &client.Snapshot{ 441 SHA3_384: map[string]string{}, 442 } 443 c.Assert(backend.AddDirToZip(context.Background(), snapshot, z, "", "an/entry", d), check.IsNil) 444 z.Close() // write out the central directory 445 446 c.Check(snapshot.SHA3_384, check.HasLen, 1) 447 c.Check(snapshot.SHA3_384["an/entry"], check.HasLen, 96) 448 c.Check(snapshot.Size > 0, check.Equals, true) // actual size most likely system-dependent 449 br := bytes.NewReader(buf.Bytes()) 450 r, err := zip.NewReader(br, int64(br.Len())) 451 c.Assert(err, check.IsNil) 452 c.Check(r.File, check.HasLen, 1) 453 c.Check(r.File[0].Name, check.Equals, "an/entry") 454 } 455 456 func (s *snapshotSuite) TestHappyRoundtrip(c *check.C) { 457 s.testHappyRoundtrip(c, "marker", false) 458 } 459 460 func (s *snapshotSuite) TestHappyRoundtripAutomaticSnapshot(c *check.C) { 461 s.testHappyRoundtrip(c, "marker", true) 462 } 463 464 func (s *snapshotSuite) TestHappyRoundtripNoCommon(c *check.C) { 465 for _, t := range table(snap.MinimalPlaceInfo("hello-snap", snap.R(42)), filepath.Join(dirs.GlobalRootDir, "home/snapuser")) { 466 if _, d := filepath.Split(t.dir); d == "common" { 467 c.Assert(os.RemoveAll(t.dir), check.IsNil) 468 } 469 } 470 s.testHappyRoundtrip(c, "marker", false) 471 } 472 473 func (s *snapshotSuite) TestHappyRoundtripNoRev(c *check.C) { 474 for _, t := range table(snap.MinimalPlaceInfo("hello-snap", snap.R(42)), filepath.Join(dirs.GlobalRootDir, "home/snapuser")) { 475 if _, d := filepath.Split(t.dir); d == "42" { 476 c.Assert(os.RemoveAll(t.dir), check.IsNil) 477 } 478 } 479 s.testHappyRoundtrip(c, "../common/marker", false) 480 } 481 482 func (s *snapshotSuite) testHappyRoundtrip(c *check.C, marker string, auto bool) { 483 if os.Geteuid() == 0 { 484 c.Skip("this test cannot run as root (runuser will fail)") 485 } 486 logger.SimpleSetup() 487 488 epoch := snap.E("42*") 489 info := &snap.Info{SideInfo: snap.SideInfo{RealName: "hello-snap", Revision: snap.R(42), SnapID: "hello-id"}, Version: "v1.33", Epoch: epoch} 490 cfg := map[string]interface{}{"some-setting": false} 491 shID := uint64(12) 492 493 shw, err := backend.Save(context.TODO(), shID, info, cfg, []string{"snapuser"}, &backend.Flags{Auto: auto}) 494 c.Assert(err, check.IsNil) 495 c.Check(shw.SetID, check.Equals, shID) 496 c.Check(shw.Snap, check.Equals, info.InstanceName()) 497 c.Check(shw.SnapID, check.Equals, info.SnapID) 498 c.Check(shw.Version, check.Equals, info.Version) 499 c.Check(shw.Epoch, check.DeepEquals, epoch) 500 c.Check(shw.Revision, check.Equals, info.Revision) 501 c.Check(shw.Conf, check.DeepEquals, cfg) 502 c.Check(shw.Auto, check.Equals, auto) 503 c.Check(backend.Filename(shw), check.Equals, filepath.Join(dirs.SnapshotsDir, "12_hello-snap_v1.33_42.zip")) 504 c.Check(hashkeys(shw), check.DeepEquals, []string{"archive.tgz", "user/snapuser.tgz"}) 505 506 shs, err := backend.List(context.TODO(), 0, nil) 507 c.Assert(err, check.IsNil) 508 c.Assert(shs, check.HasLen, 1) 509 c.Assert(shs[0].Snapshots, check.HasLen, 1) 510 511 shr, err := backend.Open(backend.Filename(shw)) 512 c.Assert(err, check.IsNil) 513 defer shr.Close() 514 515 for label, sh := range map[string]*client.Snapshot{"open": &shr.Snapshot, "list": shs[0].Snapshots[0]} { 516 comm := check.Commentf("%q", label) 517 c.Check(sh.SetID, check.Equals, shID, comm) 518 c.Check(sh.Snap, check.Equals, info.InstanceName(), comm) 519 c.Check(sh.SnapID, check.Equals, info.SnapID, comm) 520 c.Check(sh.Version, check.Equals, info.Version, comm) 521 c.Check(sh.Epoch, check.DeepEquals, epoch) 522 c.Check(sh.Revision, check.Equals, info.Revision, comm) 523 c.Check(sh.Conf, check.DeepEquals, cfg, comm) 524 c.Check(sh.SHA3_384, check.DeepEquals, shw.SHA3_384, comm) 525 c.Check(sh.Auto, check.Equals, auto) 526 } 527 c.Check(shr.Name(), check.Equals, filepath.Join(dirs.SnapshotsDir, "12_hello-snap_v1.33_42.zip")) 528 c.Check(shr.Check(context.TODO(), nil), check.IsNil) 529 530 newroot := c.MkDir() 531 c.Assert(os.MkdirAll(filepath.Join(newroot, "home/snapuser"), 0755), check.IsNil) 532 dirs.SetRootDir(newroot) 533 534 var diff = func() *exec.Cmd { 535 cmd := exec.Command("diff", "-urN", "-x*.zip", s.root, newroot) 536 // cmd.Stdout = os.Stdout 537 // cmd.Stderr = os.Stderr 538 return cmd 539 } 540 541 for i := 0; i < 3; i++ { 542 comm := check.Commentf("%d", i) 543 // sanity check 544 c.Check(diff().Run(), check.NotNil, comm) 545 546 // restore leaves things like they were (again and again) 547 rs, err := shr.Restore(context.TODO(), snap.R(0), nil, logger.Debugf) 548 c.Assert(err, check.IsNil, comm) 549 rs.Cleanup() 550 c.Check(diff().Run(), check.IsNil, comm) 551 552 // dirty it -> no longer like it was 553 c.Check(ioutil.WriteFile(filepath.Join(info.DataDir(), marker), []byte("scribble\n"), 0644), check.IsNil, comm) 554 } 555 } 556 557 func (s *snapshotSuite) TestRestoreRoundtripDifferentRevision(c *check.C) { 558 if os.Geteuid() == 0 { 559 c.Skip("this test cannot run as root (runuser will fail)") 560 } 561 logger.SimpleSetup() 562 563 epoch := snap.E("42*") 564 info := &snap.Info{SideInfo: snap.SideInfo{RealName: "hello-snap", Revision: snap.R(42), SnapID: "hello-id"}, Version: "v1.33", Epoch: epoch} 565 shID := uint64(12) 566 567 shw, err := backend.Save(context.TODO(), shID, info, nil, []string{"snapuser"}, nil) 568 c.Assert(err, check.IsNil) 569 c.Check(shw.Revision, check.Equals, info.Revision) 570 571 shr, err := backend.Open(backend.Filename(shw)) 572 c.Assert(err, check.IsNil) 573 defer shr.Close() 574 575 c.Check(shr.Revision, check.Equals, info.Revision) 576 c.Check(shr.Name(), check.Equals, filepath.Join(dirs.SnapshotsDir, "12_hello-snap_v1.33_42.zip")) 577 578 // move the expected data to its expected place 579 for _, dir := range []string{ 580 filepath.Join(s.root, "home", "snapuser", "snap", "hello-snap"), 581 filepath.Join(dirs.SnapDataDir, "hello-snap"), 582 } { 583 c.Check(os.Rename(filepath.Join(dir, "42"), filepath.Join(dir, "17")), check.IsNil) 584 } 585 586 newroot := c.MkDir() 587 c.Assert(os.MkdirAll(filepath.Join(newroot, "home", "snapuser"), 0755), check.IsNil) 588 dirs.SetRootDir(newroot) 589 590 var diff = func() *exec.Cmd { 591 cmd := exec.Command("diff", "-urN", "-x*.zip", s.root, newroot) 592 // cmd.Stdout = os.Stdout 593 // cmd.Stderr = os.Stderr 594 return cmd 595 } 596 597 // sanity check 598 c.Check(diff().Run(), check.NotNil) 599 600 // restore leaves things like they were, but in the new dir 601 rs, err := shr.Restore(context.TODO(), snap.R("17"), nil, logger.Debugf) 602 c.Assert(err, check.IsNil) 603 rs.Cleanup() 604 c.Check(diff().Run(), check.IsNil) 605 } 606 607 func (s *snapshotSuite) TestPickUserWrapperRunuser(c *check.C) { 608 n := 0 609 defer backend.MockExecLookPath(func(s string) (string, error) { 610 n++ 611 if s != "runuser" { 612 c.Fatalf(`expected to get "runuser", got %q`, s) 613 } 614 return "/sbin/runuser", nil 615 })() 616 617 c.Check(backend.PickUserWrapper(), check.Equals, "/sbin/runuser") 618 c.Check(n, check.Equals, 1) 619 } 620 621 func (s *snapshotSuite) TestPickUserWrapperSudo(c *check.C) { 622 n := 0 623 defer backend.MockExecLookPath(func(s string) (string, error) { 624 n++ 625 if n == 1 { 626 if s != "runuser" { 627 c.Fatalf(`expected to get "runuser" first, got %q`, s) 628 } 629 return "", errors.New("no such thing") 630 } 631 if s != "sudo" { 632 c.Fatalf(`expected to get "sudo" next, got %q`, s) 633 } 634 return "/usr/bin/sudo", nil 635 })() 636 637 c.Check(backend.PickUserWrapper(), check.Equals, "/usr/bin/sudo") 638 c.Check(n, check.Equals, 2) 639 } 640 641 func (s *snapshotSuite) TestPickUserWrapperNothing(c *check.C) { 642 n := 0 643 defer backend.MockExecLookPath(func(s string) (string, error) { 644 n++ 645 return "", errors.New("no such thing") 646 })() 647 648 c.Check(backend.PickUserWrapper(), check.Equals, "") 649 c.Check(n, check.Equals, 2) 650 } 651 652 func (s *snapshotSuite) TestMaybeRunuserHappyRunuser(c *check.C) { 653 uid := sys.UserID(0) 654 defer backend.MockSysGeteuid(func() sys.UserID { return uid })() 655 defer backend.SetUserWrapper("/sbin/runuser")() 656 logbuf, restore := logger.MockLogger() 657 defer restore() 658 659 c.Check(backend.TarAsUser("test", "--bar"), check.DeepEquals, &exec.Cmd{ 660 Path: "/sbin/runuser", 661 Args: []string{"/sbin/runuser", "-u", "test", "--", "tar", "--bar"}, 662 }) 663 c.Check(backend.TarAsUser("root", "--bar"), check.DeepEquals, &exec.Cmd{ 664 Path: s.tarPath, 665 Args: []string{"tar", "--bar"}, 666 }) 667 uid = 42 668 c.Check(backend.TarAsUser("test", "--bar"), check.DeepEquals, &exec.Cmd{ 669 Path: s.tarPath, 670 Args: []string{"tar", "--bar"}, 671 }) 672 c.Check(logbuf.String(), check.Equals, "") 673 } 674 675 func (s *snapshotSuite) TestMaybeRunuserHappySudo(c *check.C) { 676 uid := sys.UserID(0) 677 defer backend.MockSysGeteuid(func() sys.UserID { return uid })() 678 defer backend.SetUserWrapper("/usr/bin/sudo")() 679 logbuf, restore := logger.MockLogger() 680 defer restore() 681 682 cmd := backend.TarAsUser("test", "--bar") 683 c.Check(cmd, check.DeepEquals, &exec.Cmd{ 684 Path: "/usr/bin/sudo", 685 Args: []string{"/usr/bin/sudo", "-u", "test", "--", "tar", "--bar"}, 686 }) 687 c.Check(backend.TarAsUser("root", "--bar"), check.DeepEquals, &exec.Cmd{ 688 Path: s.tarPath, 689 Args: []string{"tar", "--bar"}, 690 }) 691 uid = 42 692 c.Check(backend.TarAsUser("test", "--bar"), check.DeepEquals, &exec.Cmd{ 693 Path: s.tarPath, 694 Args: []string{"tar", "--bar"}, 695 }) 696 c.Check(logbuf.String(), check.Equals, "") 697 } 698 699 func (s *snapshotSuite) TestMaybeRunuserNoHappy(c *check.C) { 700 uid := sys.UserID(0) 701 defer backend.MockSysGeteuid(func() sys.UserID { return uid })() 702 defer backend.SetUserWrapper("")() 703 logbuf, restore := logger.MockLogger() 704 defer restore() 705 706 c.Check(backend.TarAsUser("test", "--bar"), check.DeepEquals, &exec.Cmd{ 707 Path: s.tarPath, 708 Args: []string{"tar", "--bar"}, 709 }) 710 c.Check(backend.TarAsUser("root", "--bar"), check.DeepEquals, &exec.Cmd{ 711 Path: s.tarPath, 712 Args: []string{"tar", "--bar"}, 713 }) 714 uid = 42 715 c.Check(backend.TarAsUser("test", "--bar"), check.DeepEquals, &exec.Cmd{ 716 Path: s.tarPath, 717 Args: []string{"tar", "--bar"}, 718 }) 719 c.Check(strings.TrimSpace(logbuf.String()), check.Matches, ".* No user wrapper found.*") 720 }