queryresult.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. package source
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. "github.com/opencost/opencost/core/pkg/log"
  7. "github.com/opencost/opencost/core/pkg/util"
  8. )
  9. // QueryResultsChan is a channel of query results
  10. type QueryResultsChan chan *QueryResults
  11. // Await returns query results, blocking until they are made available, and
  12. // deferring the closure of the underlying channel
  13. func (qrc QueryResultsChan) Await() ([]*QueryResult, error) {
  14. defer close(qrc)
  15. results := <-qrc
  16. if results.Error != nil {
  17. return nil, results.Error
  18. }
  19. return results.Results, nil
  20. }
  21. // ResultKeys is a "configuration" struct that contains the keys/labels used to resolve labeled query
  22. // results. ResultKeys can be defined with every QueryResults instance if necessary, and alter the keys
  23. // used to fetch results when calling the following methods on QueryResults:
  24. //
  25. // GetCluster()
  26. // GetNamespace()
  27. // GetNode()
  28. // GetInstance()
  29. // GetInstanceType()
  30. // GetContainer()
  31. // GetPod()
  32. // GetProviderID()
  33. // GetDevice()
  34. type ResultKeys struct {
  35. ClusterKey string
  36. NamespaceKey string
  37. NodeKey string
  38. InstanceKey string
  39. InstanceTypeKey string
  40. ContainerKey string
  41. PodKey string
  42. ProviderIDKey string
  43. DeviceKey string
  44. }
  45. // DefaultResultKeys returns a new ResultKeys instance with typical default values.
  46. func DefaultResultKeys() *ResultKeys {
  47. return &ResultKeys{
  48. ClusterKey: ClusterIDLabel,
  49. NamespaceKey: NamespaceLabel,
  50. NodeKey: NodeLabel,
  51. InstanceKey: InstanceLabel,
  52. InstanceTypeKey: InstanceTypeLabel,
  53. ContainerKey: ContainerLabel,
  54. PodKey: PodLabel,
  55. ProviderIDKey: ProviderIDLabel,
  56. DeviceKey: DeviceLabel,
  57. }
  58. }
  59. // ClusterKeyWithDefaults returns a new ResultKeys instance with the provided cluster key and the
  60. // rest of the keys set to their default values.
  61. func ClusterKeyWithDefaults(clusterKey string) *ResultKeys {
  62. keys := DefaultResultKeys()
  63. keys.ClusterKey = clusterKey
  64. return keys
  65. }
  66. // QueryResults contains all of the query results and the source query string.
  67. type QueryResults struct {
  68. Query string
  69. Error error
  70. Results []*QueryResult
  71. }
  72. func NewQueryResults(query string) *QueryResults {
  73. return &QueryResults{
  74. Query: query,
  75. }
  76. }
  77. // QueryResult contains a single result from a prometheus query. It's common
  78. // to refer to query results as a slice of QueryResult
  79. type QueryResult struct {
  80. Metric map[string]interface{} `json:"metric"`
  81. Values []*util.Vector `json:"values"`
  82. keys *ResultKeys
  83. }
  84. func NewQueryResult(metrics map[string]any, values []*util.Vector, keys *ResultKeys) *QueryResult {
  85. if keys == nil {
  86. keys = DefaultResultKeys()
  87. }
  88. return &QueryResult{
  89. Metric: metrics,
  90. Values: values,
  91. keys: keys,
  92. }
  93. }
  94. func (qr *QueryResult) GetCluster() (string, error) {
  95. return qr.GetString(qr.keys.ClusterKey)
  96. }
  97. func (qr *QueryResult) GetNamespace() (string, error) {
  98. return qr.GetString(qr.keys.NamespaceKey)
  99. }
  100. func (qr *QueryResult) GetNode() (string, error) {
  101. return qr.GetString(qr.keys.NodeKey)
  102. }
  103. func (qr *QueryResult) GetInstance() (string, error) {
  104. return qr.GetString(qr.keys.InstanceKey)
  105. }
  106. func (qr *QueryResult) GetInstanceType() (string, error) {
  107. return qr.GetString(qr.keys.InstanceTypeKey)
  108. }
  109. func (qr *QueryResult) GetContainer() (string, error) {
  110. value, err := qr.GetString(qr.keys.ContainerKey)
  111. if value == "" || err != nil {
  112. alternate, e := qr.GetString(qr.keys.ContainerKey + "_name")
  113. if alternate == "" || e != nil {
  114. return "", fmt.Errorf("'%s' and '%s' fields do not exist in data result vector", qr.keys.ContainerKey, qr.keys.ContainerKey+"_name")
  115. }
  116. return alternate, nil
  117. }
  118. return value, nil
  119. }
  120. func (qr *QueryResult) GetPod() (string, error) {
  121. value, err := qr.GetString(qr.keys.PodKey)
  122. if value == "" || err != nil {
  123. alternate, e := qr.GetString(qr.keys.PodKey + "_name")
  124. if alternate == "" || e != nil {
  125. return "", fmt.Errorf("'%s' and '%s' fields do not exist in data result vector", qr.keys.PodKey, qr.keys.PodKey+"_name")
  126. }
  127. return alternate, nil
  128. }
  129. return value, nil
  130. }
  131. func (qr *QueryResult) GetProviderID() (string, error) {
  132. return qr.GetString(qr.keys.ProviderIDKey)
  133. }
  134. func (qr *QueryResult) GetDevice() (string, error) {
  135. return qr.GetString(qr.keys.DeviceKey)
  136. }
  137. // GetString returns the requested field, or an error if it does not exist
  138. func (qr *QueryResult) GetString(field string) (string, error) {
  139. f, ok := qr.Metric[field]
  140. if !ok {
  141. return "", fmt.Errorf("'%s' field does not exist in data result vector", field)
  142. }
  143. strField, ok := f.(string)
  144. if !ok {
  145. return "", fmt.Errorf("'%s' field is improperly formatted and cannot be converted to string", field)
  146. }
  147. return strField, nil
  148. }
  149. func (qr *QueryResult) GetBool(field string) (bool, error) {
  150. f, ok := qr.Metric[field]
  151. if !ok {
  152. return false, fmt.Errorf("'%s' field does not exist in data result vector", field)
  153. }
  154. switch f.(type) {
  155. case bool:
  156. return f.(bool), nil
  157. case string:
  158. b, err := strconv.ParseBool(f.(string))
  159. if err != nil {
  160. return false, fmt.Errorf("string value for field could not be parsed to bool: %w", err)
  161. }
  162. return b, nil
  163. }
  164. return false, fmt.Errorf("field did not have an appropriate type for bool conversion: %T", f)
  165. }
  166. // GetStrings returns the requested fields, or an error if it does not exist
  167. func (qr *QueryResult) GetStrings(fields ...string) (map[string]string, error) {
  168. values := map[string]string{}
  169. for _, field := range fields {
  170. f, ok := qr.Metric[field]
  171. if !ok {
  172. return nil, fmt.Errorf("'%s' field does not exist in data result vector", field)
  173. }
  174. value, ok := f.(string)
  175. if !ok {
  176. return nil, fmt.Errorf("'%s' field is improperly formatted and cannot be converted to string", field)
  177. }
  178. values[field] = value
  179. }
  180. return values, nil
  181. }
  182. // GetLabels returns all labels and their values from the query result
  183. func (qr *QueryResult) GetLabels() map[string]string {
  184. result := make(map[string]string)
  185. // Find All keys with prefix label_, remove prefix, add to labels
  186. for k, v := range qr.Metric {
  187. if !strings.HasPrefix(k, "label_") {
  188. continue
  189. }
  190. label := strings.TrimPrefix(k, "label_")
  191. value, ok := v.(string)
  192. if !ok {
  193. log.Warnf("Failed to parse label value for label: '%s'", label)
  194. continue
  195. }
  196. result[label] = value
  197. }
  198. return result
  199. }
  200. // GetAnnotations returns all annotations and their values from the query result
  201. func (qr *QueryResult) GetAnnotations() map[string]string {
  202. result := make(map[string]string)
  203. // Find All keys with prefix annotation_, remove prefix, add to annotations
  204. for k, v := range qr.Metric {
  205. if !strings.HasPrefix(k, "annotation_") {
  206. continue
  207. }
  208. annotations := strings.TrimPrefix(k, "annotation_")
  209. value, ok := v.(string)
  210. if !ok {
  211. log.Warnf("Failed to parse label value for label: '%s'", annotations)
  212. continue
  213. }
  214. result[annotations] = value
  215. }
  216. return result
  217. }