storage.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package storage
  2. import (
  3. "os"
  4. "strings"
  5. "time"
  6. "github.com/opencost/opencost/core/pkg/log"
  7. "github.com/pkg/errors"
  8. )
  9. // DirDelim is the delimiter used to model a directory structure in an object store bucket.
  10. const DirDelim = "/"
  11. // DoesNotExistError is used as a generic error to return when a target path does not
  12. // exist in storage. Equivalent to os.ErrorNotExist such that it will work with os.IsNotExist(err)
  13. var DoesNotExistError = os.ErrNotExist
  14. // StorageInfo is a data object containing basic information about the path in storage.
  15. type StorageInfo struct {
  16. Name string // base name of the file
  17. Size int64 // length in bytes for regular files
  18. ModTime time.Time // modification time
  19. }
  20. // Storage provides an API for storing binary data
  21. type Storage interface {
  22. // StorageType returns a string identifier for the type of storage used by the implementation.
  23. StorageType() StorageType
  24. // FullPath returns the storage working path combined with the path provided
  25. FullPath(path string) string
  26. // Stat returns the StorageStats for the specific path.
  27. Stat(path string) (*StorageInfo, error)
  28. // Read uses the relative path of the storage combined with the provided path to
  29. // read the contents.
  30. Read(path string) ([]byte, error)
  31. // Write uses the relative path of the storage combined with the provided path
  32. // to write a new file or overwrite an existing file.
  33. Write(path string, data []byte) error
  34. // Remove uses the relative path of the storage combined with the provided path to
  35. // remove a file from storage permanently.
  36. Remove(path string) error
  37. // Exists uses the relative path of the storage combined with the provided path to
  38. // determine if the file exists.
  39. Exists(path string) (bool, error)
  40. // List uses the relative path of the storage combined with the provided path to return
  41. // storage information for the files.
  42. List(path string) ([]*StorageInfo, error)
  43. // ListDirectories uses the relative path of the storage combined with the provided path
  44. // to return storage information for only directories contained along the path. This
  45. // functions as List, but returns storage information for only directories.
  46. ListDirectories(path string) ([]*StorageInfo, error)
  47. }
  48. // Validate uses the provided storage implementation to write a test file to the store, followed by a removal.
  49. func Validate(storage Storage, validateWriteDelete bool) error {
  50. const testPath = "tmp/test.txt"
  51. const testContent = "test"
  52. log.Debug("validating storage")
  53. // attempt to read a path
  54. _, err := storage.Exists(testPath)
  55. if err != nil {
  56. return errors.Wrap(err, "Failed to check if path exists")
  57. }
  58. // attempt to list a path
  59. _, err = storage.List(testPath)
  60. if err != nil {
  61. return errors.Wrap(err, "Failed to list path")
  62. }
  63. if validateWriteDelete {
  64. // attempt to write a path
  65. err = storage.Write(testPath, []byte(testContent))
  66. if err != nil {
  67. return errors.Wrap(err, "Failed to write data to storage")
  68. }
  69. }
  70. // attempt to read the path
  71. // If we are not validating write and delete, the file won't exist since we never wrote it.
  72. // We only want to check read permissions, so ignore errors with "exist" and "404" in the error message to bypass the file not exist error.
  73. data, err := storage.Read(testPath)
  74. if err != nil && !strings.Contains(err.Error(), "exist") && !strings.Contains(err.Error(), "404") {
  75. return errors.Wrap(err, "Failed to read data from storage")
  76. }
  77. if validateWriteDelete {
  78. if string(data) != testContent {
  79. return errors.New("Failed to read the expected data from storage")
  80. }
  81. // delete the path
  82. err = storage.Remove(testPath)
  83. if err != nil {
  84. return errors.Wrap(err, "Failed to remove data from storage")
  85. }
  86. }
  87. return nil
  88. }
  89. // IsNotExist returns true if the error provided from a storage object is DoesNotExist
  90. func IsNotExist(err error) bool {
  91. if err == nil {
  92. return false
  93. }
  94. return err.Error() == DoesNotExistError.Error()
  95. }