networkcosts.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package costmodel
  2. import (
  3. costAnalyzerCloud "github.com/kubecost/cost-model/pkg/cloud"
  4. "github.com/kubecost/cost-model/pkg/util"
  5. "k8s.io/klog"
  6. )
  7. // NetworkUsageVNetworkUsageDataector contains the network usage values for egress network traffic
  8. type NetworkUsageData struct {
  9. ClusterID string
  10. PodName string
  11. Namespace string
  12. NetworkZoneEgress []*util.Vector
  13. NetworkRegionEgress []*util.Vector
  14. NetworkInternetEgress []*util.Vector
  15. }
  16. // NetworkUsageVector contains a network usage vector for egress network traffic
  17. type NetworkUsageVector struct {
  18. ClusterID string
  19. PodName string
  20. Namespace string
  21. Values []*util.Vector
  22. }
  23. // GetNetworkUsageData performs a join of the the results of zone, region, and internet usage queries to return a single
  24. // map containing network costs for each namespace+pod
  25. func GetNetworkUsageData(zr interface{}, rr interface{}, ir interface{}, defaultClusterID string) (map[string]*NetworkUsageData, error) {
  26. zoneNetworkMap, err := getNetworkUsage(zr, defaultClusterID)
  27. if err != nil {
  28. return nil, err
  29. }
  30. regionNetworkMap, err := getNetworkUsage(rr, defaultClusterID)
  31. if err != nil {
  32. return nil, err
  33. }
  34. internetNetworkMap, err := getNetworkUsage(ir, defaultClusterID)
  35. if err != nil {
  36. return nil, err
  37. }
  38. usageData := make(map[string]*NetworkUsageData)
  39. for k, v := range zoneNetworkMap {
  40. existing, ok := usageData[k]
  41. if !ok {
  42. usageData[k] = &NetworkUsageData{
  43. ClusterID: v.ClusterID,
  44. PodName: v.PodName,
  45. Namespace: v.Namespace,
  46. NetworkZoneEgress: v.Values,
  47. }
  48. continue
  49. }
  50. existing.NetworkZoneEgress = v.Values
  51. }
  52. for k, v := range regionNetworkMap {
  53. existing, ok := usageData[k]
  54. if !ok {
  55. usageData[k] = &NetworkUsageData{
  56. ClusterID: v.ClusterID,
  57. PodName: v.PodName,
  58. Namespace: v.Namespace,
  59. NetworkRegionEgress: v.Values,
  60. }
  61. continue
  62. }
  63. existing.NetworkRegionEgress = v.Values
  64. }
  65. for k, v := range internetNetworkMap {
  66. existing, ok := usageData[k]
  67. if !ok {
  68. usageData[k] = &NetworkUsageData{
  69. ClusterID: v.ClusterID,
  70. PodName: v.PodName,
  71. Namespace: v.Namespace,
  72. NetworkInternetEgress: v.Values,
  73. }
  74. continue
  75. }
  76. existing.NetworkInternetEgress = v.Values
  77. }
  78. return usageData, nil
  79. }
  80. // GetNetworkCost computes the actual cost for NetworkUsageData based on data provided by the Provider.
  81. func GetNetworkCost(usage *NetworkUsageData, cloud costAnalyzerCloud.Provider) ([]*util.Vector, error) {
  82. var results []*util.Vector
  83. pricing, err := cloud.NetworkPricing()
  84. if err != nil {
  85. return nil, err
  86. }
  87. zoneCost := pricing.ZoneNetworkEgressCost
  88. regionCost := pricing.RegionNetworkEgressCost
  89. internetCost := pricing.InternetNetworkEgressCost
  90. zlen := len(usage.NetworkZoneEgress)
  91. rlen := len(usage.NetworkRegionEgress)
  92. ilen := len(usage.NetworkInternetEgress)
  93. l := max(zlen, rlen, ilen)
  94. for i := 0; i < l; i++ {
  95. var cost float64 = 0
  96. var timestamp float64
  97. if i < zlen {
  98. cost += usage.NetworkZoneEgress[i].Value * zoneCost
  99. timestamp = usage.NetworkZoneEgress[i].Timestamp
  100. }
  101. if i < rlen {
  102. cost += usage.NetworkRegionEgress[i].Value * regionCost
  103. timestamp = usage.NetworkRegionEgress[i].Timestamp
  104. }
  105. if i < ilen {
  106. cost += usage.NetworkInternetEgress[i].Value * internetCost
  107. timestamp = usage.NetworkInternetEgress[i].Timestamp
  108. }
  109. results = append(results, &util.Vector{
  110. Value: cost,
  111. Timestamp: timestamp,
  112. })
  113. }
  114. return results, nil
  115. }
  116. func getNetworkUsage(qr interface{}, defaultClusterID string) (map[string]*NetworkUsageVector, error) {
  117. ncdmap := make(map[string]*NetworkUsageVector)
  118. result, err := NewQueryResults(qr)
  119. if err != nil {
  120. return nil, err
  121. }
  122. for _, val := range result {
  123. podName, err := val.GetString("pod_name")
  124. if err != nil {
  125. return nil, err
  126. }
  127. namespace, err := val.GetString("namespace")
  128. if err != nil {
  129. return nil, err
  130. }
  131. clusterID, err := val.GetString("cluster_id")
  132. if clusterID == "" {
  133. klog.V(4).Info("Prometheus vector does not have cluster id")
  134. clusterID = defaultClusterID
  135. }
  136. key := namespace + "," + podName + "," + clusterID
  137. ncdmap[key] = &NetworkUsageVector{
  138. ClusterID: clusterID,
  139. Namespace: namespace,
  140. PodName: podName,
  141. Values: val.Values,
  142. }
  143. }
  144. return ncdmap, nil
  145. }
  146. func max(x int, rest ...int) int {
  147. curr := x
  148. for _, v := range rest {
  149. if v > curr {
  150. curr = v
  151. }
  152. }
  153. return curr
  154. }