memorystorage.go 4.9 KB

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