networkcosts.go 4.3 KB

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