1package fakes 2 3import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "os" 9 gopath "path" 10 "path/filepath" 11 "sort" 12 "strings" 13 "sync" 14 "syscall" 15 "time" 16 17 gouuid "github.com/nu7hatch/gouuid" 18 19 bosherr "github.com/cloudfoundry/bosh-utils/errors" 20 boshsys "github.com/cloudfoundry/bosh-utils/system" 21) 22 23type FakeFileType string 24 25type removeAllFn func(path string) error 26 27type globFn func(pattern string) ([]string, error) 28 29const ( 30 FakeFileTypeFile FakeFileType = "file" 31 FakeFileTypeSymlink FakeFileType = "symlink" 32 FakeFileTypeDir FakeFileType = "dir" 33) 34 35type FakeFileSystem struct { 36 fileRegistry *FakeFileStatsRegistry 37 filesLock sync.Mutex 38 39 HomeDirUsername string 40 HomeDirHomePath string 41 42 ExpandPathPath string 43 ExpandPathExpanded string 44 ExpandPathErr error 45 46 openFileRegistry *FakeFileRegistry 47 OpenFileErr error 48 49 ReadFileError error 50 ReadFileWithOptsCallCount int 51 readFileErrorByPath map[string]error 52 53 WriteFileError error 54 WriteFileErrors map[string]error 55 WriteFileCallCount int 56 WriteFileQuietlyCallCount int 57 58 SymlinkError error 59 60 MkdirAllError error 61 mkdirAllErrorByPath map[string]error 62 MkdirAllCallCount int 63 64 ChangeTempRootErr error 65 66 ChownErr error 67 ChownCallCount int 68 ChmodErr error 69 ChmodCallCount int 70 71 CopyFileError error 72 CopyFileCallCount int 73 74 CopyDirError error 75 76 RenameError error 77 RenameOldPaths []string 78 RenameNewPaths []string 79 80 RemoveAllStub removeAllFn 81 82 ReadAndFollowLinkError error 83 ReadlinkError error 84 85 StatWithOptsCallCount int 86 StatCallCount int 87 88 TempFileError error 89 TempFileErrorsByPrefix map[string]error 90 ReturnTempFile boshsys.File 91 ReturnTempFiles []boshsys.File 92 ReturnTempFilesByPrefix map[string]boshsys.File 93 94 TempDirDir string 95 TempDirDirs []string 96 TempDirError error 97 98 GlobErr error 99 GlobStub globFn 100 GlobErrs map[string]error 101 globsMap map[string][][]string 102 103 WalkErr error 104 105 TempRootPath string 106 strictTempRoot bool 107} 108 109type FakeFileStats struct { 110 FileType FakeFileType 111 112 FileMode os.FileMode 113 Flags int 114 Username string 115 Groupname string 116 117 ModTime time.Time 118 Open bool 119 120 SymlinkTarget string 121 122 Content []byte 123} 124 125func (stats FakeFileStats) StringContents() string { 126 return string(stats.Content) 127} 128 129type FakeFileInfo struct { 130 os.FileInfo 131 file FakeFile 132} 133 134func (fi FakeFileInfo) Mode() os.FileMode { 135 return fi.file.Stats.FileMode 136} 137 138func (fi FakeFileInfo) ModTime() time.Time { 139 return fi.file.Stats.ModTime 140} 141 142func (fi FakeFileInfo) Size() int64 { 143 return int64(len(fi.file.Contents)) 144} 145 146func (fi FakeFileInfo) IsDir() bool { 147 return fi.file.Stats.FileType == FakeFileTypeDir 148} 149 150type FakeFile struct { 151 path string 152 fs *FakeFileSystem 153 154 Stats *FakeFileStats 155 156 WriteErr error 157 Contents []byte 158 159 ReadErr error 160 ReadAtErr error 161 readIndex int64 162 163 CloseErr error 164 165 StatErr error 166} 167 168func NewFakeFile(path string, fs *FakeFileSystem) *FakeFile { 169 fakeFile := &FakeFile{ 170 path: path, 171 fs: fs, 172 } 173 me := fs.fileRegistry.Get(path) 174 if me != nil { 175 fakeFile.Contents = me.Content 176 fakeFile.Stats = me 177 fakeFile.Stats.Open = true 178 } 179 return fakeFile 180} 181 182func (f *FakeFile) Name() string { 183 return f.path 184} 185 186func (f *FakeFile) Write(contents []byte) (int, error) { 187 if f.WriteErr != nil { 188 return 0, f.WriteErr 189 } 190 191 f.fs.filesLock.Lock() 192 defer f.fs.filesLock.Unlock() 193 194 stats := f.fs.getOrCreateFile(f.path) 195 stats.Content = contents 196 197 f.Contents = contents 198 return len(contents), nil 199} 200 201func (f *FakeFile) Read(b []byte) (int, error) { 202 if f.readIndex >= int64(len(f.Contents)) { 203 return 0, io.EOF 204 } 205 copy(b, f.Contents) 206 f.readIndex = int64(len(f.Contents)) 207 return len(f.Contents), f.ReadErr 208} 209 210func (f *FakeFile) ReadAt(b []byte, offset int64) (int, error) { 211 copy(b, f.Contents[offset:]) 212 return len(f.Contents[offset:]), f.ReadAtErr 213} 214 215func (f *FakeFile) WriteAt(b []byte, offset int64) (int, error) { 216 return len(b), nil 217} 218 219func (f *FakeFile) Seek(int64, int) (int64, error) { 220 return 0, nil 221} 222 223func (f *FakeFile) Close() error { 224 if f.Stats != nil { 225 f.Stats.Open = false 226 } 227 return f.CloseErr 228} 229 230func (f FakeFile) Stat() (os.FileInfo, error) { 231 return FakeFileInfo{file: f}, f.StatErr 232} 233 234func NewFakeFileSystem() *FakeFileSystem { 235 return &FakeFileSystem{ 236 fileRegistry: NewFakeFileStatsRegistry(), 237 openFileRegistry: NewFakeFileRegistry(), 238 GlobErrs: map[string]error{}, 239 globsMap: map[string][][]string{}, 240 readFileErrorByPath: map[string]error{}, 241 mkdirAllErrorByPath: map[string]error{}, 242 WriteFileErrors: map[string]error{}, 243 TempFileErrorsByPrefix: map[string]error{}, 244 } 245} 246 247func (fs *FakeFileSystem) GetFileTestStat(path string) *FakeFileStats { 248 fs.filesLock.Lock() 249 defer fs.filesLock.Unlock() 250 251 return fs.fileRegistry.Get(path) 252} 253 254func (fs *FakeFileSystem) HomeDir(username string) (string, error) { 255 fs.HomeDirUsername = username 256 return fs.HomeDirHomePath, nil 257} 258 259func (fs *FakeFileSystem) ExpandPath(path string) (string, error) { 260 fs.ExpandPathPath = path 261 if fs.ExpandPathExpanded == "" { 262 return fs.ExpandPathPath, fs.ExpandPathErr 263 } 264 265 return fs.ExpandPathExpanded, fs.ExpandPathErr 266} 267 268func (fs *FakeFileSystem) RegisterMkdirAllError(path string, err error) { 269 path = gopath.Join(path) 270 if _, ok := fs.mkdirAllErrorByPath[path]; ok { 271 panic(fmt.Sprintf("MkdirAll error is already set for path: %s", path)) 272 } 273 fs.mkdirAllErrorByPath[path] = err 274} 275 276func (fs *FakeFileSystem) MkdirAll(path string, perm os.FileMode) error { 277 fs.MkdirAllCallCount++ 278 fs.filesLock.Lock() 279 defer fs.filesLock.Unlock() 280 281 if fs.MkdirAllError != nil { 282 return fs.MkdirAllError 283 } 284 285 path = gopath.Join(path) 286 287 if fs.mkdirAllErrorByPath[path] != nil { 288 return fs.mkdirAllErrorByPath[path] 289 } 290 291 return fs.mkdir(path, perm) 292} 293 294func (fs *FakeFileSystem) mkdir(path string, perm os.FileMode) error { 295 if path == "." { 296 return nil 297 } 298 299 if !atRoot(path) { 300 parent := filepath.Dir(path) 301 // We can't use any functions which require the filesystem lock. 302 parentStats := fs.fileRegistry.Get(parent) 303 304 if parentStats != nil && parentStats.FileType == FakeFileTypeFile { 305 return fmt.Errorf("cannot create a directory in a file (%s)", path) 306 } 307 308 // Parent does not exist 309 if parentStats == nil { 310 if err := fs.mkdir(parent, perm); err != nil { 311 return err 312 } 313 } 314 } 315 316 stats := fs.getOrCreateFile(path) 317 stats.FileMode = perm 318 stats.FileType = FakeFileTypeDir 319 fs.fileRegistry.Register(path, stats) 320 return nil 321} 322 323func atRoot(path string) bool { 324 switch path { 325 case "/": 326 return true 327 case filepath.VolumeName(path) + "\\": 328 return true 329 default: 330 return false 331 } 332} 333 334func (fs *FakeFileSystem) RegisterOpenFile(path string, file *FakeFile) { 335 path = gopath.Join(path) 336 fs.openFileRegistry.Register(path, file) 337} 338 339func (fs *FakeFileSystem) FindFileStats(path string) (*FakeFileStats, error) { 340 if stats := fs.fileRegistry.Get(path); stats != nil { 341 return stats, nil 342 } 343 return nil, fmt.Errorf("Path does not exist: %s", path) 344} 345 346func (fs *FakeFileSystem) OpenFile(path string, flag int, perm os.FileMode) (boshsys.File, error) { 347 fs.filesLock.Lock() 348 defer fs.filesLock.Unlock() 349 350 if fs.OpenFileErr != nil { 351 return nil, fs.OpenFileErr 352 } 353 354 // Make sure to record a reference for FileExist, etc. to work 355 stats := fs.getOrCreateFile(path) 356 stats.FileMode = perm 357 stats.Flags = flag 358 stats.FileType = FakeFileTypeFile 359 360 openFile := fs.openFileRegistry.Get(path) 361 if openFile != nil { 362 return openFile, nil 363 } 364 file := NewFakeFile(path, fs) 365 366 fs.RegisterOpenFile(path, file) 367 return file, nil 368} 369 370func (fs *FakeFileSystem) Stat(path string) (os.FileInfo, error) { 371 fs.StatCallCount++ 372 return fs.StatHelper(path) 373} 374 375func (fs *FakeFileSystem) StatWithOpts(path string, opts boshsys.StatOpts) (os.FileInfo, error) { 376 fs.StatWithOptsCallCount++ 377 return fs.StatHelper(path) 378} 379 380func (fs *FakeFileSystem) StatHelper(path string) (os.FileInfo, error) { 381 fs.filesLock.Lock() 382 defer fs.filesLock.Unlock() 383 384 openFile := fs.openFileRegistry.Get(path) 385 if openFile != nil { 386 return openFile.Stat() 387 } 388 389 stats := fs.fileRegistry.Get(path) 390 if stats == nil { 391 panic(fmt.Sprintf("Unexpected Stat call for path '%s' that does not exist", path)) 392 } 393 394 if stats.FileType == FakeFileTypeSymlink { 395 targetStats := fs.fileRegistry.Get(stats.SymlinkTarget) 396 if targetStats == nil { 397 return nil, fmt.Errorf("stat: %s: no such file or directory", path) 398 } 399 400 stats = targetStats 401 } 402 403 return NewFakeFile(path, fs).Stat() 404} 405func (fs *FakeFileSystem) Readlink(symlinkPath string) (string, error) { 406 targetPath, err := fs.readlink(symlinkPath) 407 if err != nil { 408 return targetPath, err 409 } 410 411 //Converts internal path formatting (which is UNIX/Linux based) to native OS file system path 412 //This emulates the real behavior of how the real file system returns symlink 413 if strings.HasPrefix(targetPath, "/") { 414 absFilePath, err := filepath.Abs(targetPath) 415 return absFilePath, err 416 } 417 418 return targetPath, err 419} 420 421func (fs *FakeFileSystem) readlink(path string) (string, error) { 422 if fs.ReadlinkError != nil { 423 return "", fs.ReadlinkError 424 } 425 426 fs.filesLock.Lock() 427 defer fs.filesLock.Unlock() 428 429 stats := fs.fileRegistry.Get(path) 430 if stats == nil { 431 return "", os.ErrNotExist 432 } 433 434 if stats.FileType != FakeFileTypeSymlink { 435 return "", errors.New(fmt.Sprintf("cannot readlink of non-symlink")) 436 } 437 438 return stats.SymlinkTarget, nil 439} 440 441func (fs *FakeFileSystem) Lstat(path string) (os.FileInfo, error) { 442 fs.filesLock.Lock() 443 defer fs.filesLock.Unlock() 444 445 openFile := fs.openFileRegistry.Get(path) 446 if openFile != nil { 447 return openFile.Stat() 448 } 449 450 stats := fs.fileRegistry.Get(path) 451 if stats == nil { 452 panic(fmt.Sprintf("Unexpected Stat call for path '%s' that does not exist", path)) 453 } 454 455 return NewFakeFile(path, fs).Stat() 456} 457 458func (fs *FakeFileSystem) Chown(path, username string) error { 459 fs.ChownCallCount++ 460 fs.filesLock.Lock() 461 defer fs.filesLock.Unlock() 462 463 // check early to avoid requiring file presence 464 if fs.ChownErr != nil { 465 return fs.ChownErr 466 } 467 468 stats := fs.fileRegistry.Get(path) 469 if stats == nil { 470 return fmt.Errorf("Path does not exist: %s", path) 471 } 472 473 parts := strings.Split(username, ":") 474 stats.Username = parts[0] 475 stats.Groupname = parts[0] 476 if len(parts) > 1 { 477 stats.Groupname = parts[1] 478 } 479 return nil 480} 481 482func (fs *FakeFileSystem) Chmod(path string, perm os.FileMode) error { 483 fs.ChmodCallCount++ 484 fs.filesLock.Lock() 485 defer fs.filesLock.Unlock() 486 487 // check early to avoid requiring file presence 488 if fs.ChmodErr != nil { 489 return fs.ChmodErr 490 } 491 492 stats := fs.fileRegistry.Get(path) 493 if stats == nil { 494 return fmt.Errorf("Path does not exist: %s", path) 495 } 496 497 stats.FileMode = perm 498 return nil 499} 500 501func (fs *FakeFileSystem) WriteFileString(path, content string) error { 502 return fs.WriteFile(path, []byte(content)) 503} 504 505func (fs *FakeFileSystem) WriteFileQuietly(path string, content []byte) error { 506 fs.WriteFileQuietlyCallCount++ 507 return fs.writeFile(path, content) 508} 509 510func (fs *FakeFileSystem) WriteFile(path string, content []byte) error { 511 fs.WriteFileCallCount++ 512 return fs.writeFile(path, content) 513} 514 515func (fs *FakeFileSystem) writeFile(path string, content []byte) error { 516 fs.filesLock.Lock() 517 defer fs.filesLock.Unlock() 518 519 err := fs.WriteFileError 520 if err != nil { 521 return err 522 } 523 524 err = fs.WriteFileErrors[path] 525 if err != nil { 526 return err 527 } 528 529 path = fs.fileRegistry.UnifiedPath(path) 530 parent := gopath.Dir(path) 531 if parent != "." { 532 fs.writeDir(parent) 533 } 534 535 stats := fs.getOrCreateFile(path) 536 stats.FileType = FakeFileTypeFile 537 stats.Content = content 538 return nil 539} 540 541func (fs *FakeFileSystem) writeDir(path string) error { 542 parent := gopath.Dir(path) 543 544 grandparent := gopath.Dir(parent) 545 if grandparent != parent { 546 fs.writeDir(parent) 547 } 548 549 stats := fs.getOrCreateFile(path) 550 stats.FileType = FakeFileTypeDir 551 return nil 552} 553 554func (fs *FakeFileSystem) ConvergeFileContents(path string, content []byte, opts ...boshsys.ConvergeFileContentsOpts) (bool, error) { 555 fs.filesLock.Lock() 556 defer fs.filesLock.Unlock() 557 558 if fs.WriteFileError != nil { 559 return false, fs.WriteFileError 560 } 561 562 err := fs.WriteFileErrors[path] 563 if err != nil { 564 return false, err 565 } 566 567 if len(opts) > 0 && opts[0].DryRun { 568 stats := fs.fileRegistry.Get(path) 569 if stats == nil { 570 return true, nil 571 } 572 return bytes.Compare(stats.Content, content) != 0, nil 573 } 574 575 stats := fs.getOrCreateFile(path) 576 stats.FileType = FakeFileTypeFile 577 578 if bytes.Compare(stats.Content, content) != 0 { 579 stats.Content = content 580 return true, nil 581 } 582 583 return false, nil 584} 585 586func (fs *FakeFileSystem) ReadFileString(path string) (string, error) { 587 bytes, err := fs.ReadFile(path) 588 if err != nil { 589 return "", err 590 } 591 592 return string(bytes), nil 593} 594 595func (fs *FakeFileSystem) RegisterReadFileError(path string, err error) { 596 if _, ok := fs.readFileErrorByPath[path]; ok { 597 panic(fmt.Sprintf("ReadFile error is already set for path: %s", path)) 598 } 599 fs.readFileErrorByPath[path] = err 600} 601 602func (fs *FakeFileSystem) UnregisterReadFileError(path string) { 603 delete(fs.readFileErrorByPath, path) 604} 605 606func (fs *FakeFileSystem) ReadFileWithOpts(path string, opts boshsys.ReadOpts) ([]byte, error) { 607 fs.ReadFileWithOptsCallCount++ 608 return fs.ReadFile(path) 609} 610 611func (fs *FakeFileSystem) ReadFile(path string) ([]byte, error) { 612 stats := fs.GetFileTestStat(path) 613 if stats != nil { 614 if fs.ReadFileError != nil { 615 return nil, fs.ReadFileError 616 } 617 618 if fs.readFileErrorByPath[path] != nil { 619 return nil, fs.readFileErrorByPath[path] 620 } 621 622 return stats.Content, nil 623 } 624 625 return nil, bosherr.ComplexError{ 626 Err: bosherr.Error("Not found"), 627 Cause: &os.PathError{ 628 Op: "open", 629 Path: path, 630 Err: syscall.ENOENT, 631 }, 632 } 633} 634 635func (fs *FakeFileSystem) FileExists(path string) bool { 636 return fs.GetFileTestStat(path) != nil 637} 638 639func (fs *FakeFileSystem) Rename(oldPath, newPath string) error { 640 fs.filesLock.Lock() 641 defer fs.filesLock.Unlock() 642 643 if fs.RenameError != nil { 644 return fs.RenameError 645 } 646 647 oldPath = fs.fileRegistry.UnifiedPath(oldPath) 648 newPath = fs.fileRegistry.UnifiedPath(newPath) 649 650 parentDir := gopath.Dir(newPath) 651 if parentDir != "." && fs.fileRegistry.Get(parentDir) == nil { 652 return errors.New("Parent directory does not exist") 653 } 654 655 stats := fs.fileRegistry.Get(oldPath) 656 if stats == nil { 657 return errors.New("Old path did not exist") 658 } 659 660 fs.RenameOldPaths = append(fs.RenameOldPaths, oldPath) 661 fs.RenameNewPaths = append(fs.RenameNewPaths, newPath) 662 663 for filePath, fileStats := range fs.fileRegistry.GetAll() { 664 if filePath == oldPath { 665 fs.fileRegistry.Register(newPath, fileStats) 666 } else if strings.HasPrefix(filePath, fmt.Sprintf("%s/", oldPath)) { 667 dstPath := gopath.Join(newPath, filePath[len(oldPath):]) 668 fs.fileRegistry.Register(dstPath, fileStats) 669 } 670 } 671 672 // Ignore error from RemoveAll 673 fs.removeAll(oldPath) 674 675 return nil 676} 677 678func (fs *FakeFileSystem) Symlink(oldPath, newPath string) (err error) { 679 fs.filesLock.Lock() 680 defer fs.filesLock.Unlock() 681 682 if fs.SymlinkError == nil { 683 stats := fs.getOrCreateFile(newPath) 684 stats.FileMode |= os.ModeSymlink 685 stats.FileType = FakeFileTypeSymlink 686 stats.SymlinkTarget = fs.fileRegistry.UnifiedPath(oldPath) 687 return 688 } 689 690 err = fs.SymlinkError 691 return 692} 693 694func (fs *FakeFileSystem) ReadAndFollowLink(symlinkPath string) (string, error) { 695 targetPath, err := fs.readAndFollowLink(symlinkPath) 696 if err != nil { 697 return targetPath, err 698 } 699 700 //Converts internal path formatting (which is UNIX/Linux based) to native OS file system path 701 //This emulates the real behavior of how the real file system returns symlink 702 if strings.HasPrefix(targetPath, "/") { 703 absFilePath, err := filepath.Abs(targetPath) 704 return absFilePath, err 705 } 706 707 return targetPath, err 708} 709 710func (fs *FakeFileSystem) readAndFollowLink(symlinkPath string) (string, error) { 711 if fs.ReadAndFollowLinkError != nil { 712 return "", fs.ReadAndFollowLinkError 713 } 714 715 if symlinkPath == "\\" { 716 symlinkPath = "/" 717 } 718 719 if symlinkPath == "" || 720 symlinkPath == "/" || 721 symlinkPath == filepath.VolumeName(symlinkPath)+"\\" { 722 return symlinkPath, nil 723 } 724 725 if symlinkPath == "." { 726 return fs.fileRegistry.UnifiedPath("."), nil 727 } 728 729 symlinkPath = filepath.Join(symlinkPath) 730 731 stat := fs.GetFileTestStat(symlinkPath) 732 if stat == nil { 733 return "", os.ErrNotExist 734 } 735 736 if stat.FileType != FakeFileTypeSymlink { 737 dirPath, err := fs.readAndFollowLink(filepath.Dir(symlinkPath)) 738 if err != nil { 739 return "", err 740 } 741 742 return gopath.Join(dirPath, filepath.Base(symlinkPath)), nil 743 } 744 745 if gopath.IsAbs(stat.SymlinkTarget) { 746 return fs.readAndFollowLink(stat.SymlinkTarget) 747 } 748 749 dirPath, err := fs.readAndFollowLink(filepath.Dir(symlinkPath)) 750 if err != nil { 751 return "", err 752 } 753 754 return fs.readAndFollowLink(gopath.Join(dirPath, stat.SymlinkTarget)) 755} 756 757func (fs *FakeFileSystem) CopyFile(srcPath, dstPath string) error { 758 fs.CopyFileCallCount++ 759 fs.filesLock.Lock() 760 defer fs.filesLock.Unlock() 761 762 if fs.CopyFileError != nil { 763 return fs.CopyFileError 764 } 765 766 srcFile := fs.fileRegistry.Get(srcPath) 767 if srcFile == nil { 768 return errors.New(fmt.Sprintf("%s doesn't exist", srcPath)) 769 } 770 771 fs.fileRegistry.Register(dstPath, srcFile) 772 return nil 773} 774 775func (fs *FakeFileSystem) CopyDir(srcPath, dstPath string) error { 776 fs.filesLock.Lock() 777 defer fs.filesLock.Unlock() 778 779 if fs.CopyDirError != nil { 780 return fs.CopyDirError 781 } 782 783 srcPath = fs.fileRegistry.UnifiedPath(srcPath) 784 dstPath = fs.fileRegistry.UnifiedPath(dstPath) 785 786 for filePath, fileStats := range fs.fileRegistry.GetAll() { 787 if filePath == srcPath { 788 fs.fileRegistry.Register(dstPath, fileStats) 789 } else if strings.HasPrefix(filePath, fmt.Sprintf("%s/", srcPath)) { 790 dstPath := gopath.Join(dstPath, filePath[len(srcPath):]) 791 fs.fileRegistry.Register(dstPath, fileStats) 792 } 793 } 794 795 return nil 796} 797 798func (fs *FakeFileSystem) ChangeTempRoot(tempRootPath string) error { 799 if fs.ChangeTempRootErr != nil { 800 return fs.ChangeTempRootErr 801 } 802 fs.TempRootPath = tempRootPath 803 return nil 804} 805 806func (fs *FakeFileSystem) EnableStrictTempRootBehavior() { 807 fs.strictTempRoot = true 808} 809 810func (fs *FakeFileSystem) TempFile(prefix string) (file boshsys.File, err error) { 811 fs.filesLock.Lock() 812 defer fs.filesLock.Unlock() 813 814 if fs.TempFileError != nil { 815 return nil, fs.TempFileError 816 } 817 818 if fs.TempFileErrorsByPrefix[prefix] != nil { 819 return nil, fs.TempFileErrorsByPrefix[prefix] 820 } 821 822 if fs.strictTempRoot && fs.TempRootPath == "" { 823 return nil, errors.New("Temp file was requested without having set a temp root") 824 } 825 826 if fs.ReturnTempFilesByPrefix != nil { 827 file = fs.ReturnTempFilesByPrefix[prefix] 828 } else if fs.ReturnTempFile != nil { 829 file = fs.ReturnTempFile 830 } else if len(fs.ReturnTempFiles) != 0 { 831 file = fs.ReturnTempFiles[0] 832 fs.ReturnTempFiles = fs.ReturnTempFiles[1:] 833 } else { 834 file, err = os.Open(os.DevNull) 835 if err != nil { 836 err = bosherr.WrapError(err, fmt.Sprintf("Opening %s", os.DevNull)) 837 return 838 } 839 } 840 841 // Make sure to record a reference for FileExist, etc. to work 842 stats := fs.getOrCreateFile(file.Name()) 843 stats.FileType = FakeFileTypeFile 844 return 845} 846 847func (fs *FakeFileSystem) TempDir(prefix string) (string, error) { 848 fs.filesLock.Lock() 849 defer fs.filesLock.Unlock() 850 851 if fs.TempDirError != nil { 852 return "", fs.TempDirError 853 } 854 855 if fs.strictTempRoot && fs.TempRootPath == "" { 856 return "", errors.New("Temp file was requested without having set a temp root") 857 } 858 859 var path string 860 if len(fs.TempDirDir) > 0 { 861 path = fs.TempDirDir 862 } else if fs.TempDirDirs != nil { 863 if len(fs.TempDirDirs) == 0 { 864 return "", errors.New("Failed to create new temp dir: TempDirDirs is empty") 865 } 866 path = fs.TempDirDirs[0] 867 fs.TempDirDirs = fs.TempDirDirs[1:] 868 } else { 869 uuid, err := gouuid.NewV4() 870 if err != nil { 871 return "", err 872 } 873 874 path = uuid.String() 875 } 876 877 // Make sure to record a reference for FileExist, etc. to work 878 stats := fs.getOrCreateFile(path) 879 stats.FileType = FakeFileTypeDir 880 881 return path, nil 882} 883 884func (fs *FakeFileSystem) RemoveAll(path string) error { 885 if path == "" { 886 panic("RemoveAll requires path") 887 } 888 889 if fs.RemoveAllStub != nil { 890 err := fs.RemoveAllStub(path) 891 if err != nil { 892 return err 893 } 894 } 895 896 fs.filesLock.Lock() 897 defer fs.filesLock.Unlock() 898 899 path = fs.fileRegistry.UnifiedPath(path) 900 return fs.removeAll(path) 901} 902 903func (fs *FakeFileSystem) removeAll(path string) error { 904 fileInfo := fs.fileRegistry.Get(path) 905 if fileInfo != nil { 906 fs.fileRegistry.Remove(path) 907 if fileInfo.FileType != FakeFileTypeDir { 908 return nil 909 } 910 } 911 912 // path must be a dir 913 path = path + "/" 914 915 filesToRemove := []string{} 916 for name := range fs.fileRegistry.GetAll() { 917 if strings.HasPrefix(name, path) { 918 filesToRemove = append(filesToRemove, name) 919 } 920 } 921 for _, name := range filesToRemove { 922 fs.fileRegistry.Remove(name) 923 } 924 925 return nil 926} 927 928func (fs *FakeFileSystem) Glob(pattern string) (matches []string, err error) { 929 if fs.GlobStub != nil { 930 matches, err = fs.GlobStub(pattern) 931 if err != nil { 932 return nil, err 933 } else { 934 return matches, nil 935 } 936 } 937 938 remainingMatches, found := fs.globsMap[pattern] 939 if found { 940 matches = remainingMatches[0] 941 if len(remainingMatches) > 1 { 942 fs.globsMap[pattern] = remainingMatches[1:] 943 } 944 } else { 945 matches = []string{} 946 } 947 if err, ok := fs.GlobErrs[pattern]; ok { 948 return matches, err 949 } 950 return matches, fs.GlobErr 951} 952 953func (fs *FakeFileSystem) RecursiveGlob(pattern string) (matches []string, err error) { 954 return fs.Glob(pattern) 955} 956 957func (fs *FakeFileSystem) Walk(root string, walkFunc filepath.WalkFunc) error { 958 if fs.WalkErr != nil { 959 return walkFunc("", nil, fs.WalkErr) 960 } 961 962 var paths []string 963 for path := range fs.fileRegistry.GetAll() { 964 paths = append(paths, path) 965 } 966 sort.Strings(paths) 967 968 root = gopath.Join(root) + "/" 969 for _, path := range paths { 970 fileStats := fs.fileRegistry.Get(path) 971 if strings.HasPrefix(path, root) { 972 fakeFile := NewFakeFile(path, fs) 973 fakeFile.Stats = fileStats 974 fileInfo, _ := fakeFile.Stat() 975 err := walkFunc(path, fileInfo, nil) 976 if err != nil { 977 return err 978 } 979 } 980 } 981 982 return nil 983} 984 985func (fs *FakeFileSystem) SetGlob(pattern string, matches ...[]string) { 986 fs.globsMap[pattern] = matches 987} 988 989func (fs *FakeFileSystem) getOrCreateFile(path string) *FakeFileStats { 990 stats := fs.fileRegistry.Get(path) 991 if stats == nil { 992 stats = new(FakeFileStats) 993 fs.fileRegistry.Register(path, stats) 994 } 995 return stats 996} 997 998type FakeFileStatsRegistry struct { 999 files map[string]*FakeFileStats 1000} 1001 1002func NewFakeFileStatsRegistry() *FakeFileStatsRegistry { 1003 return &FakeFileStatsRegistry{ 1004 files: map[string]*FakeFileStats{}, 1005 } 1006} 1007 1008func (fsr *FakeFileStatsRegistry) Register(path string, stats *FakeFileStats) { 1009 fsr.files[fsr.UnifiedPath(path)] = stats 1010} 1011 1012func (fsr *FakeFileStatsRegistry) Get(path string) *FakeFileStats { 1013 return fsr.files[fsr.UnifiedPath(path)] 1014} 1015 1016func (fsr *FakeFileStatsRegistry) GetAll() map[string]*FakeFileStats { 1017 return fsr.files 1018} 1019 1020func (fsr *FakeFileStatsRegistry) Remove(path string) { 1021 delete(fsr.files, fsr.UnifiedPath(path)) 1022} 1023 1024func (fsr *FakeFileStatsRegistry) UnifiedPath(path string) string { 1025 path = strings.TrimPrefix(path, filepath.VolumeName(path)) 1026 return filepath.ToSlash(gopath.Join(path)) 1027} 1028 1029type FakeFileRegistry struct { 1030 files map[string]*FakeFile 1031} 1032 1033func NewFakeFileRegistry() *FakeFileRegistry { 1034 return &FakeFileRegistry{ 1035 files: map[string]*FakeFile{}, 1036 } 1037} 1038 1039func (ffr *FakeFileRegistry) Register(path string, file *FakeFile) { 1040 ffr.files[ffr.UnifiedPath(path)] = file 1041} 1042 1043func (ffr *FakeFileRegistry) Get(path string) *FakeFile { 1044 return ffr.files[ffr.UnifiedPath(path)] 1045} 1046 1047func (ffr *FakeFileRegistry) UnifiedPath(path string) string { 1048 path = strings.TrimPrefix(path, filepath.VolumeName(path)) 1049 return filepath.ToSlash(gopath.Join(path)) 1050} 1051