storage.go 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. package pricingmodel
  2. import (
  3. "fmt"
  4. "strings"
  5. "time"
  6. "github.com/opencost/opencost/core/pkg/exporter"
  7. "github.com/opencost/opencost/core/pkg/exporter/pathing"
  8. "github.com/opencost/opencost/core/pkg/log"
  9. "github.com/opencost/opencost/core/pkg/model/pricingmodel"
  10. "github.com/opencost/opencost/core/pkg/pipelines"
  11. "github.com/opencost/opencost/core/pkg/storage"
  12. )
  13. // storageWriter wraps a Storage backend with a StaticFileStoragePathFormatter,
  14. // translating source keys into full storage paths on write.
  15. type storageWriter struct {
  16. store storage.Storage
  17. encoder exporter.Encoder[pricingmodel.PricingModelSet]
  18. pathing *pathing.StaticFileStoragePathFormatter
  19. }
  20. func newStorageWriter(store storage.Storage, appName string) (*storageWriter, error) {
  21. p, err := pathing.NewStaticFileStoragePathFormatter(appName, pipelines.PricingModelPipelineName)
  22. if err != nil {
  23. return nil, fmt.Errorf("newStorageWriter: failed to create path formatter: %w", err)
  24. }
  25. return &storageWriter{
  26. store: store,
  27. encoder: exporter.NewBingenFileEncoder[pricingmodel.PricingModelSet](),
  28. pathing: p,
  29. }, nil
  30. }
  31. func (sw *storageWriter) Write(pms *pricingmodel.PricingModelSet) error {
  32. fullPath := sw.pathing.ToFullPath("", pms.SourceKey, sw.encoder.FileExt())
  33. data, err := sw.encoder.Encode(pms)
  34. if err != nil {
  35. return fmt.Errorf("failed to encode data: %w", err)
  36. }
  37. err = sw.store.Write(fullPath, data)
  38. if err != nil {
  39. return fmt.Errorf("failed to write to storage: %w", err)
  40. }
  41. log.Infof("PricingModel[%s]: exported pricing model set (%d bytes)", pms.SourceKey, len(data))
  42. return nil
  43. }
  44. // LastUpdates returns a map of source key to last modified time for each file
  45. // found under the formatter's directory. Source keys are reconstructed as the
  46. // file path relative to Dir().
  47. func (sw *storageWriter) LastUpdates() (map[string]time.Time, error) {
  48. result := make(map[string]time.Time)
  49. dir := sw.pathing.Dir()
  50. files, err := sw.store.List(dir)
  51. if err != nil && !storage.IsNotExist(err) {
  52. return nil, fmt.Errorf("collectModTimes: listing %s: %w", dir, err)
  53. }
  54. for _, f := range files {
  55. nameParts := strings.Split(f.Name, ".")
  56. key := nameParts[0]
  57. if modTime, ok := result[key]; ok && modTime.After(f.ModTime) {
  58. continue
  59. }
  60. result[key] = f.ModTime
  61. }
  62. return result, nil
  63. }