memorystorage.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  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. // String returns the storage type as a string for logging purposes.
  24. func (ms *MemoryStorage) String() string {
  25. return string(ms.StorageType())
  26. }
  27. // StorageType returns a string identifier for the type of storage used by the implementation.
  28. func (ms *MemoryStorage) StorageType() StorageType {
  29. return StorageTypeMemory
  30. }
  31. // FullPath returns the storage working path combined with the path provided
  32. func (ms *MemoryStorage) FullPath(path string) string {
  33. return path
  34. }
  35. // Stat returns the StorageStats for the specific path.
  36. func (ms *MemoryStorage) Stat(path string) (*StorageInfo, error) {
  37. ms.lock.Lock()
  38. defer ms.lock.Unlock()
  39. path = filepath.Clean(path)
  40. if file, ok := ms.directPaths[path]; ok {
  41. return &StorageInfo{
  42. Name: file.Name,
  43. Size: file.Size(),
  44. ModTime: file.ModTime,
  45. }, nil
  46. }
  47. return nil, fmt.Errorf("file not found: %s - %w", path, DoesNotExistError)
  48. }
  49. // Read uses the relative path of the storage combined with the provided path to
  50. // read the contents.
  51. func (ms *MemoryStorage) Read(path string) ([]byte, error) {
  52. ms.lock.Lock()
  53. defer ms.lock.Unlock()
  54. path = filepath.Clean(path)
  55. if file, ok := ms.directPaths[path]; ok {
  56. return file.Contents, nil
  57. }
  58. return nil, fmt.Errorf("file not found: %s - %w", path, DoesNotExistError)
  59. }
  60. // Write uses the relative path of the storage combined with the provided path
  61. // to write a new file or overwrite an existing file.
  62. func (ms *MemoryStorage) Write(path string, data []byte) error {
  63. ms.lock.Lock()
  64. defer ms.lock.Unlock()
  65. paths, pFile := memfile.Split(path)
  66. f := memfile.NewMemoryFile(pFile, data)
  67. currentDir := memfile.CreateSubdirectory(ms.fileTree, paths)
  68. currentDir.AddFile(f)
  69. ms.directPaths[path] = f
  70. return nil
  71. }
  72. // Remove uses the relative path of the storage combined with the provided path to
  73. // remove a file from storage permanently.
  74. func (ms *MemoryStorage) Remove(path string) error {
  75. ms.lock.Lock()
  76. defer ms.lock.Unlock()
  77. path = filepath.Clean(path)
  78. paths, pFile := memfile.Split(path)
  79. currentDir, err := memfile.FindSubdirectory(ms.fileTree, paths)
  80. if err != nil {
  81. return fmt.Errorf("file not found: %s - %w", path, DoesNotExistError)
  82. }
  83. currentDir.RemoveFile(pFile)
  84. delete(ms.directPaths, path)
  85. return nil
  86. }
  87. // Exists uses the relative path of the storage combined with the provided path to
  88. // determine if the file exists.
  89. func (ms *MemoryStorage) Exists(path string) (bool, error) {
  90. ms.lock.Lock()
  91. defer ms.lock.Unlock()
  92. path = filepath.Clean(path)
  93. _, ok := ms.directPaths[path]
  94. return ok, nil
  95. }
  96. // List uses the relative path of the storage combined with the provided path to return
  97. // storage information for the files.
  98. func (ms *MemoryStorage) List(path string) ([]*StorageInfo, error) {
  99. ms.lock.Lock()
  100. defer ms.lock.Unlock()
  101. paths := memfile.SplitPaths(path)
  102. currentDir, err := memfile.FindSubdirectory(ms.fileTree, paths)
  103. if err != nil {
  104. // contract for bucket storages returns an empty list in this case
  105. // so just log a warning, and return an empty list
  106. log.Warnf("failed to resolve path: %s - %s", path, err)
  107. return []*StorageInfo{}, nil
  108. }
  109. storageInfos := make([]*StorageInfo, 0, currentDir.FileCount())
  110. for f := range currentDir.Files() {
  111. storageInfos = append(storageInfos, &StorageInfo{
  112. Name: f.Name,
  113. Size: f.Size(),
  114. ModTime: f.ModTime,
  115. })
  116. }
  117. return storageInfos, nil
  118. }
  119. // ListDirectories uses the relative path of the storage combined with the provided path
  120. // to return storage information for only directories contained along the path. This
  121. // functions as List, but returns storage information for only directories.
  122. func (ms *MemoryStorage) ListDirectories(path string) ([]*StorageInfo, error) {
  123. ms.lock.Lock()
  124. defer ms.lock.Unlock()
  125. paths := memfile.SplitPaths(path)
  126. currentDir, err := memfile.FindSubdirectory(ms.fileTree, paths)
  127. if err != nil {
  128. // contract for bucket storages returns an empty list in this case
  129. // so just log a warning, and return an empty list
  130. log.Warnf("failed to resolve path: %s - %s", path, err)
  131. return []*StorageInfo{}, nil
  132. }
  133. storageInfos := make([]*StorageInfo, 0, currentDir.DirCount())
  134. for d := range currentDir.Directories() {
  135. storageInfos = append(storageInfos, &StorageInfo{
  136. Name: filepath.Join(append(paths, d.Name)...) + "/",
  137. Size: d.Size(),
  138. ModTime: d.ModTime,
  139. })
  140. }
  141. return storageInfos, nil
  142. }