module.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. package usd
  2. import (
  3. "context"
  4. "embed"
  5. "fmt"
  6. "io"
  7. "github.com/opencost/opencost/core/pkg/pricing"
  8. "github.com/opencost/opencost/core/pkg/reader"
  9. "github.com/opencost/opencost/core/pkg/unit"
  10. )
  11. // PricingModule must satisfy the pricing.PricingModule interface
  12. var _ pricing.PricingModule = (*PricingModule)(nil)
  13. //go:embed *.jsonl
  14. var embeddedFS embed.FS
  15. type PricingModule struct{}
  16. // NewPricingModule creates a new USD pricing module backed by embedded JSONL files.
  17. func NewPricingModule() (*PricingModule, error) {
  18. return &PricingModule{}, nil
  19. }
  20. func (pm *PricingModule) newNodeReader() (reader.Reader[*pricing.NodePricing], error) {
  21. f, err := embeddedFS.Open("nodes.jsonl")
  22. if err != nil {
  23. return nil, fmt.Errorf("opening embedded nodes.jsonl: %w", err)
  24. }
  25. return reader.NewJSONLinesReader[*pricing.NodePricing](f), nil
  26. }
  27. func (pm *PricingModule) newPVReader() (reader.Reader[*pricing.PersistentVolumePricing], error) {
  28. f, err := embeddedFS.Open("persistentvolumes.jsonl")
  29. if err != nil {
  30. return nil, fmt.Errorf("opening embedded persistentvolumes.jsonl: %w", err)
  31. }
  32. return reader.NewJSONLinesReader[*pricing.PersistentVolumePricing](f), nil
  33. }
  34. func (pm *PricingModule) newServiceReader() (reader.Reader[*pricing.ServicePricing], error) {
  35. f, err := embeddedFS.Open("services.jsonl")
  36. if err != nil {
  37. return nil, fmt.Errorf("opening embedded services.jsonl: %w", err)
  38. }
  39. return reader.NewJSONLinesReader[*pricing.ServicePricing](f), nil
  40. }
  41. func (pm *PricingModule) NewNodePricingReader(ctx context.Context) (reader.Reader[*pricing.NodePricing], error) {
  42. return pm.newNodeReader()
  43. }
  44. func (pm *PricingModule) NewPersistentVolumePricingReader(ctx context.Context) (reader.Reader[*pricing.PersistentVolumePricing], error) {
  45. return pm.newPVReader()
  46. }
  47. func (pm *PricingModule) NewClusterPricingReader(ctx context.Context) (reader.Reader[*pricing.ClusterPricing], error) {
  48. return nil, fmt.Errorf("cluster pricing not provided by public pricing module")
  49. }
  50. func (pm *PricingModule) NewNetworkPricingReader(ctx context.Context) (reader.Reader[*pricing.NetworkPricing], error) {
  51. return nil, fmt.Errorf("network pricing not provided by public pricing module")
  52. }
  53. func (pm *PricingModule) NewServicePricingReader(ctx context.Context) (reader.Reader[*pricing.ServicePricing], error) {
  54. return pm.newServiceReader()
  55. }
  56. func (pm *PricingModule) GetPricingSet(ctx context.Context) (*pricing.PricingSet, error) {
  57. ps := &pricing.PricingSet{}
  58. nodeReader, err := pm.newNodeReader()
  59. if err != nil {
  60. return nil, err
  61. }
  62. defer nodeReader.Close()
  63. dst := make([]*pricing.NodePricing, 64)
  64. for {
  65. n, err := nodeReader.Read(ctx, dst)
  66. ps.NodePricing = append(ps.NodePricing, dst[:n]...)
  67. if err == io.EOF {
  68. break
  69. }
  70. if err != nil {
  71. return nil, err
  72. }
  73. }
  74. pvReader, err := pm.newPVReader()
  75. if err != nil {
  76. return nil, err
  77. }
  78. defer pvReader.Close()
  79. pvDst := make([]*pricing.PersistentVolumePricing, 64)
  80. for {
  81. n, err := pvReader.Read(ctx, pvDst)
  82. ps.PersistentVolumePricing = append(ps.PersistentVolumePricing, pvDst[:n]...)
  83. if err == io.EOF {
  84. break
  85. }
  86. if err != nil {
  87. return nil, err
  88. }
  89. }
  90. svcReader, err := pm.newServiceReader()
  91. if err != nil {
  92. return nil, err
  93. }
  94. defer svcReader.Close()
  95. svcDst := make([]*pricing.ServicePricing, 64)
  96. for {
  97. n, err := svcReader.Read(ctx, svcDst)
  98. ps.ServicePricing = append(ps.ServicePricing, svcDst[:n]...)
  99. if err == io.EOF {
  100. break
  101. }
  102. if err != nil {
  103. return nil, err
  104. }
  105. }
  106. return ps, nil
  107. }
  108. func (pm *PricingModule) SourceKind() string {
  109. return "public"
  110. }
  111. func (pm *PricingModule) SourceName() string {
  112. return "public-usd"
  113. }
  114. func (pm *PricingModule) Checksum(ctx context.Context) (string, error) {
  115. ps, err := pm.GetPricingSet(ctx)
  116. if err != nil {
  117. return "", err
  118. }
  119. return ps.Checksum()
  120. }
  121. // Currency returns USD
  122. func Currency() unit.Currency {
  123. return unit.USD
  124. }