allocation_json.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. package opencost
  2. import (
  3. "fmt"
  4. "math"
  5. "time"
  6. "github.com/opencost/opencost/core/pkg/util/json"
  7. )
  8. // AllocationJSON exists because there are expected JSON response fields
  9. // that are calculated values from methods on an annotation
  10. type AllocationJSON struct {
  11. Name string `json:"name"`
  12. Properties *AllocationProperties `json:"properties"`
  13. Window Window `json:"window"`
  14. Start string `json:"start"`
  15. End string `json:"end"`
  16. Minutes *float64 `json:"minutes"`
  17. CPUCores *float64 `json:"cpuCores"`
  18. CPUCoreRequestAverage *float64 `json:"cpuCoreRequestAverage"`
  19. CPUCoreLimitAverage *float64 `json:"cpuCoreLimitAverage"`
  20. CPUCoreUsageAverage *float64 `json:"cpuCoreUsageAverage"`
  21. CPUCoreHours *float64 `json:"cpuCoreHours"`
  22. CPUCost *float64 `json:"cpuCost"`
  23. CPUCostAdjustment *float64 `json:"cpuCostAdjustment"`
  24. CPUCostIdle *float64 `json:"cpuCostIdle"`
  25. CPUEfficiency *float64 `json:"cpuEfficiency"`
  26. GPUCount *float64 `json:"gpuCount"`
  27. GPURequestAverage *float64 `json:"-"`
  28. GPUUsageAverage *float64 `json:"-"`
  29. GPUHours *float64 `json:"gpuHours"`
  30. GPUCost *float64 `json:"gpuCost"`
  31. GPUCostAdjustment *float64 `json:"gpuCostAdjustment"`
  32. GPUCostIdle *float64 `json:"gpuCostIdle"`
  33. GPUEfficiency *float64 `json:"gpuEfficiency"`
  34. NetworkTransferBytes *float64 `json:"networkTransferBytes"`
  35. NetworkReceiveBytes *float64 `json:"networkReceiveBytes"`
  36. NetworkCost *float64 `json:"networkCost"`
  37. NetworkCrossZoneCost *float64 `json:"networkCrossZoneCost"`
  38. NetworkCrossRegionCost *float64 `json:"networkCrossRegionCost"`
  39. NetworkInternetCost *float64 `json:"networkInternetCost"`
  40. NetworkNatGatewayEgressCost *float64 `json:"networkNatGatewayEgressCost"`
  41. NetworkNatGatewayIngressCost *float64 `json:"networkNatGatewayIngressCost"`
  42. NetworkCostAdjustment *float64 `json:"networkCostAdjustment"`
  43. LoadBalancerCost *float64 `json:"loadBalancerCost"`
  44. LoadBalancerCostAdjustment *float64 `json:"loadBalancerCostAdjustment"`
  45. PVBytes *float64 `json:"pvBytes"`
  46. PVByteHours *float64 `json:"pvByteHours"`
  47. PVCost *float64 `json:"pvCost"`
  48. PVs PVAllocations `json:"pvs"`
  49. PVCostAdjustment *float64 `json:"pvCostAdjustment"`
  50. RAMBytes *float64 `json:"ramBytes"`
  51. RAMByteRequestAverage *float64 `json:"ramByteRequestAverage"`
  52. RAMByteLimitAverage *float64 `json:"ramByteLimitAverage"`
  53. RAMByteUsageAverage *float64 `json:"ramByteUsageAverage"`
  54. RAMByteHours *float64 `json:"ramByteHours"`
  55. RAMCost *float64 `json:"ramCost"`
  56. RAMCostAdjustment *float64 `json:"ramCostAdjustment"`
  57. RAMCostIdle *float64 `json:"ramCostIdle"`
  58. RAMEfficiency *float64 `json:"ramEfficiency"`
  59. ExternalCost *float64 `json:"externalCost"`
  60. SharedCost *float64 `json:"sharedCost"`
  61. TotalCost *float64 `json:"totalCost"`
  62. TotalEfficiency *float64 `json:"totalEfficiency"`
  63. RawAllocationOnly *RawAllocationOnlyData `json:"rawAllocationOnly,omitempty"`
  64. ProportionalAssetResourceCosts *ProportionalAssetResourceCosts `json:"proportionalAssetResourceCosts,omitempty"`
  65. LoadBalancers LbAllocations `json:"lbAllocations"`
  66. SharedCostBreakdown *SharedCostBreakdowns `json:"sharedCostBreakdown,omitempty"`
  67. GPUAllocation *GPUAllocation `json:"gpuAllocation"`
  68. }
  69. func (aj *AllocationJSON) BuildFromAllocation(a *Allocation) {
  70. if aj == nil {
  71. return
  72. }
  73. aj.Name = a.Name
  74. aj.Properties = a.Properties
  75. aj.Window = a.Window
  76. aj.Start = a.Start.UTC().Format(time.RFC3339)
  77. aj.End = a.End.UTC().Format(time.RFC3339)
  78. aj.Minutes = formatFloat64ForResponse(a.Minutes())
  79. aj.CPUCores = formatFloat64ForResponse(a.CPUCores())
  80. aj.CPUCoreRequestAverage = formatFloat64ForResponse(a.CPUCoreRequestAverage)
  81. aj.CPUCoreLimitAverage = formatFloat64ForResponse(a.CPUCoreLimitAverage)
  82. aj.CPUCoreUsageAverage = formatFloat64ForResponse(a.CPUCoreUsageAverage)
  83. aj.CPUCoreHours = formatFloat64ForResponse(a.CPUCoreHours)
  84. aj.CPUCost = formatFloat64ForResponse(a.CPUCost)
  85. aj.CPUCostAdjustment = formatFloat64ForResponse(a.CPUCostAdjustment)
  86. aj.CPUCostIdle = formatFloat64ForResponse(a.CPUCostIdle)
  87. aj.CPUEfficiency = formatFloat64ForResponse(a.CPUEfficiency())
  88. aj.GPUCount = formatFloat64ForResponse(a.GPUs())
  89. aj.GPUHours = formatFloat64ForResponse(a.GPUHours)
  90. aj.GPUCost = formatFloat64ForResponse(a.GPUCost)
  91. aj.GPUCostAdjustment = formatFloat64ForResponse(a.GPUCostAdjustment)
  92. aj.GPUCostIdle = formatFloat64ForResponse(a.GPUCostIdle)
  93. aj.GPUEfficiency = formatFloat64ForResponse(a.GPUEfficiency())
  94. aj.NetworkTransferBytes = formatFloat64ForResponse(a.NetworkTransferBytes)
  95. aj.NetworkReceiveBytes = formatFloat64ForResponse(a.NetworkReceiveBytes)
  96. aj.NetworkCost = formatFloat64ForResponse(a.NetworkCost)
  97. aj.NetworkCrossZoneCost = formatFloat64ForResponse(a.NetworkCrossZoneCost)
  98. aj.NetworkCrossRegionCost = formatFloat64ForResponse(a.NetworkCrossRegionCost)
  99. aj.NetworkInternetCost = formatFloat64ForResponse(a.NetworkInternetCost)
  100. aj.NetworkNatGatewayEgressCost = formatFloat64ForResponse(a.NetworkNatGatewayEgressCost)
  101. aj.NetworkNatGatewayIngressCost = formatFloat64ForResponse(a.NetworkNatGatewayIngressCost)
  102. aj.NetworkCostAdjustment = formatFloat64ForResponse(a.NetworkCostAdjustment)
  103. aj.LoadBalancerCost = formatFloat64ForResponse(a.LoadBalancerCost)
  104. aj.LoadBalancerCostAdjustment = formatFloat64ForResponse(a.LoadBalancerCostAdjustment)
  105. aj.PVBytes = formatFloat64ForResponse(a.PVBytes())
  106. aj.PVByteHours = formatFloat64ForResponse(a.PVByteHours())
  107. aj.PVCost = formatFloat64ForResponse(a.PVCost())
  108. aj.PVs = a.PVs
  109. aj.PVCostAdjustment = formatFloat64ForResponse(a.PVCostAdjustment)
  110. aj.RAMBytes = formatFloat64ForResponse(a.RAMBytes())
  111. aj.RAMByteRequestAverage = formatFloat64ForResponse(a.RAMBytesRequestAverage)
  112. aj.RAMByteLimitAverage = formatFloat64ForResponse(a.RAMBytesLimitAverage)
  113. aj.RAMByteUsageAverage = formatFloat64ForResponse(a.RAMBytesUsageAverage)
  114. aj.RAMByteHours = formatFloat64ForResponse(a.RAMByteHours)
  115. aj.RAMCost = formatFloat64ForResponse(a.RAMCost)
  116. aj.RAMCostAdjustment = formatFloat64ForResponse(a.RAMCostAdjustment)
  117. aj.RAMCostIdle = formatFloat64ForResponse(a.RAMCostIdle)
  118. aj.RAMEfficiency = formatFloat64ForResponse(a.RAMEfficiency())
  119. aj.SharedCost = formatFloat64ForResponse(a.SharedCost)
  120. aj.ExternalCost = formatFloat64ForResponse(a.ExternalCost)
  121. aj.TotalCost = formatFloat64ForResponse(a.TotalCost())
  122. aj.TotalEfficiency = formatFloat64ForResponse(a.TotalEfficiency())
  123. aj.RawAllocationOnly = a.RawAllocationOnly
  124. aj.ProportionalAssetResourceCosts = &a.ProportionalAssetResourceCosts
  125. aj.LoadBalancers = a.LoadBalancers
  126. aj.SharedCostBreakdown = &a.SharedCostBreakdown
  127. aj.GPUAllocation = a.GPUAllocation
  128. }
  129. // formatFloat64ForResponse - take an existing float64, round it to 6 decimal places and return is possible, or return nil if invalid
  130. func formatFloat64ForResponse(f float64) *float64 {
  131. if math.IsNaN(f) || math.IsInf(f, 0) {
  132. return nil
  133. }
  134. // 6 digits of precision is the maximum the API should return
  135. result := math.Round(f*100000) / 100000.0
  136. return &result
  137. }
  138. // MarshalJSON implements json.Marshaler interface
  139. func (a *Allocation) MarshalJSON() ([]byte, error) {
  140. aj := &AllocationJSON{}
  141. aj.BuildFromAllocation(a)
  142. buffer, err := json.Marshal(aj)
  143. if err != nil {
  144. return nil, fmt.Errorf("unable to marshal allocation %s to JSON: %s", aj.Name, err)
  145. }
  146. return buffer, nil
  147. }
  148. // UnmarshalJSON prevent nil pointer on PVAllocations
  149. func (a *Allocation) UnmarshalJSON(b []byte) error {
  150. // initialize PV to prevent nil panic
  151. a.PVs = PVAllocations{}
  152. // Aliasing Allocation and casting to alias gives access to the default unmarshaller
  153. type alloc Allocation
  154. err := json.Unmarshal(b, (*alloc)(a))
  155. if err != nil {
  156. return err
  157. }
  158. // clear PVs if they are empty, it is not initialized when empty
  159. if len(a.PVs) == 0 {
  160. a.PVs = nil
  161. }
  162. return nil
  163. }
  164. // MarshalJSON marshals PVAllocation as map[*PVKey]*PVAllocation this allows PVKey to retain its values through marshalling
  165. func (pv PVAllocations) MarshalJSON() (b []byte, err error) {
  166. pointerMap := make(map[*PVKey]*PVAllocation)
  167. for pvKey, pvAlloc := range pv {
  168. kp := pvKey
  169. pointerMap[&kp] = pvAlloc
  170. }
  171. return json.Marshal(pointerMap)
  172. }
  173. // MarshalText converts PVKey to string to make it compatible with JSON Marshaller as an Object key
  174. // this function is required to have a value caller for the actual values to be saved
  175. func (pvk PVKey) MarshalText() (text []byte, err error) {
  176. return []byte(pvk.String()), nil
  177. }
  178. // UnmarshalText converts JSON key string to PVKey it compatible with JSON Unmarshaller from an Object key
  179. // this function is required to have a pointer caller for values to be pulled into marshalling struct
  180. func (pvk *PVKey) UnmarshalText(text []byte) error {
  181. return pvk.FromString(string(text))
  182. }
  183. // MarshalJSON JSON-encodes the AllocationSet
  184. func (as *AllocationSet) MarshalJSON() ([]byte, error) {
  185. if as == nil {
  186. return json.Marshal(map[string]*Allocation{})
  187. }
  188. return json.Marshal(as.Allocations)
  189. }
  190. // MarshalJSON JSON-encodes the range
  191. func (asr *AllocationSetRange) MarshalJSON() ([]byte, error) {
  192. if asr == nil {
  193. return json.Marshal([]*AllocationSet{})
  194. }
  195. return json.Marshal(asr.Allocations)
  196. }