memorystorage.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. package storage
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "sync"
  6. "github.com/opencost/opencost/core/pkg/log"
  7. "github.com/opencost/opencost/core/pkg/storage/memfile"
  8. )
  9. // MemoryStorage is a thread-safe in-memory file system storage implementation. It can be used for testing storage.Storage dependents
  10. // or to serve as a lightweight storage implementation within a production system.
  11. type MemoryStorage struct {
  12. lock sync.Mutex
  13. directPaths map[string]*memfile.MemoryFile
  14. fileTree *memfile.MemoryDirectory
  15. }
  16. // NewMemoryStorage creates a new in-memory file system storage implementation.
  17. func NewMemoryStorage() *MemoryStorage {
  18. return &MemoryStorage{
  19. directPaths: make(map[string]*memfile.MemoryFile),
  20. fileTree: memfile.NewMemoryDirectory(""),
  21. }
  22. }
  23. // StorageType returns a string identifier for the type of storage used by the implementation.
  24. func (ms *MemoryStorage) StorageType() StorageType {
  25. return StorageTypeMemory
  26. }
  27. // FullPath returns the storage working path combined with the path provided
  28. func (ms *MemoryStorage) FullPath(path string) string {
  29. return path
  30. }
  31. // Stat returns the StorageStats for the specific path.
  32. func (ms *MemoryStorage) Stat(path string) (*StorageInfo, error) {
  33. ms.lock.Lock()
  34. defer ms.lock.Unlock()
  35. path = filepath.Clean(path)
  36. if file, ok := ms.directPaths[path]; ok {
  37. return &StorageInfo{
  38. Name: file.Name,
  39. Size: file.Size(),
  40. ModTime: file.ModTime,
  41. }, nil
  42. }
  43. return nil, fmt.Errorf("file not found: %s - %w", path, DoesNotExistError)
  44. }
  45. // Read uses the relative path of the storage combined with the provided path to
  46. // read the contents.
  47. func (ms *MemoryStorage) Read(path string) ([]byte, error) {
  48. ms.lock.Lock()
  49. defer ms.lock.Unlock()
  50. path = filepath.Clean(path)
  51. if file, ok := ms.directPaths[path]; ok {
  52. return file.Contents, nil
  53. }
  54. return nil, fmt.Errorf("file not found: %s - %w", path, DoesNotExistError)
  55. }
  56. // Write uses the relative path of the storage combined with the provided path
  57. // to write a new file or overwrite an existing file.
  58. func (ms *MemoryStorage) Write(path string, data []byte) error {
  59. ms.lock.Lock()
  60. defer ms.lock.Unlock()
  61. paths, pFile := memfile.Split(path)
  62. f := memfile.NewMemoryFile(pFile, data)
  63. currentDir := memfile.CreateSubdirectory(ms.fileTree, paths)
  64. currentDir.AddFile(f)
  65. ms.directPaths[path] = f
  66. return nil
  67. }
  68. // Remove uses the relative path of the storage combined with the provided path to
  69. // remove a file from storage permanently.
  70. func (ms *MemoryStorage) Remove(path string) error {
  71. ms.lock.Lock()
  72. defer ms.lock.Unlock()
  73. path = filepath.Clean(path)
  74. paths, pFile := memfile.Split(path)
  75. currentDir, err := memfile.FindSubdirectory(ms.fileTree, paths)
  76. if err != nil {
  77. return fmt.Errorf("file not found: %s - %w", path, DoesNotExistError)
  78. }
  79. currentDir.RemoveFile(pFile)
  80. delete(ms.directPaths, path)
  81. return nil
  82. }
  83. // Exists uses the relative path of the storage combined with the provided path to
  84. // determine if the file exists.
  85. func (ms *MemoryStorage) Exists(path string) (bool, error) {
  86. ms.lock.Lock()
  87. defer ms.lock.Unlock()
  88. path = filepath.Clean(path)
  89. _, ok := ms.directPaths[path]
  90. return ok, nil
  91. }
  92. // List uses the relative path of the storage combined with the provided path to return
  93. // storage information for the files.
  94. func (ms *MemoryStorage) List(path string) ([]*StorageInfo, error) {
  95. ms.lock.Lock()
  96. defer ms.lock.Unlock()
  97. paths := memfile.SplitPaths(path)
  98. currentDir, err := memfile.FindSubdirectory(ms.fileTree, paths)
  99. if err != nil {
  100. // contract for bucket storages returns an empty list in this case
  101. // so just log a warning, and return an empty list
  102. log.Warnf("failed to resolve path: %s - %s", path, err)
  103. return []*StorageInfo{}, nil
  104. }
  105. storageInfos := make([]*StorageInfo, 0, currentDir.FileCount())
  106. for f := range currentDir.Files() {
  107. storageInfos = append(storageInfos, &StorageInfo{
  108. Name: f.Name,
  109. Size: f.Size(),
  110. ModTime: f.ModTime,
  111. })
  112. }
  113. return storageInfos, nil
  114. }
  115. // ListDirectories uses the relative path of the storage combined with the provided path
  116. // to return storage information for only directories contained along the path. This
  117. // functions as List, but returns storage information for only directories.
  118. func (ms *MemoryStorage) ListDirectories(path string) ([]*StorageInfo, error) {
  119. ms.lock.Lock()
  120. defer ms.lock.Unlock()
  121. paths := memfile.SplitPaths(path)
  122. currentDir, err := memfile.FindSubdirectory(ms.fileTree, paths)
  123. if err != nil {
  124. // contract for bucket storages returns an empty list in this case
  125. // so just log a warning, and return an empty list
  126. log.Warnf("failed to resolve path: %s - %s", path, err)
  127. return []*StorageInfo{}, nil
  128. }
  129. storageInfos := make([]*StorageInfo, 0, currentDir.DirCount())
  130. for d := range currentDir.Directories() {
  131. storageInfos = append(storageInfos, &StorageInfo{
  132. Name: filepath.Join(append(paths, d.Name)...) + "/",
  133. Size: d.Size(),
  134. ModTime: d.ModTime,
  135. })
  136. }
  137. return storageInfos, nil
  138. }