promutil.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. package promutil
  2. import (
  3. "fmt"
  4. "reflect"
  5. "regexp"
  6. "sort"
  7. "strings"
  8. "github.com/opencost/opencost/core/pkg/util/json"
  9. )
  10. var invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`)
  11. // AnyToLabels will create prometheus labels based on the fields of the interface
  12. // passed. Note that this method is quite expensive and should only be used when absolutely
  13. // necessary.
  14. func AnyToLabels(a interface{}) (map[string]string, error) {
  15. val := reflect.ValueOf(a)
  16. if val.Kind() == reflect.Map {
  17. return MapToLabels(a), nil
  18. }
  19. b, e := json.Marshal(a)
  20. if e != nil {
  21. return nil, e
  22. }
  23. var m map[string]interface{}
  24. e = json.Unmarshal(b, &m)
  25. if e != nil {
  26. return nil, e
  27. }
  28. return MapToLabels(m), nil
  29. }
  30. // MapToLabels accepts a map type, and will return a new map containing all the nested
  31. // fields separated by _ with string versions of the values.
  32. func MapToLabels(m interface{}) map[string]string {
  33. val := reflect.ValueOf(m)
  34. if val.Kind() != reflect.Map {
  35. return map[string]string{}
  36. }
  37. r := make(map[string]string)
  38. for _, k := range val.MapKeys() {
  39. key := strings.ToLower(k.String())
  40. v := val.MapIndex(k).Interface()
  41. switch v.(type) {
  42. case uint, uint8, uint16, uint32, uint64, int, int8, int16, int32, int64, string, bool, float32, float64:
  43. r[key] = fmt.Sprintf("%+v", v)
  44. default:
  45. mm := MapToLabels(v)
  46. for kk, vv := range mm {
  47. r[fmt.Sprintf("%s_%s", key, kk)] = vv
  48. }
  49. }
  50. }
  51. return r
  52. }
  53. // LabelNamesFrom accepts a mapping of labels to values and returns the label names.
  54. func LabelNamesFrom(labels map[string]string) []string {
  55. keys := []string{}
  56. for key := range labels {
  57. keys = append(keys, key)
  58. }
  59. return keys
  60. }
  61. // Prepends a qualifier string to the keys provided in the m map and returns the new keys and values.
  62. func KubePrependQualifierToLabels(m map[string]string, qualifier string) ([]string, []string) {
  63. keys := make([]string, 0, len(m))
  64. for k := range m {
  65. keys = append(keys, k)
  66. }
  67. sort.Strings(keys)
  68. values := make([]string, 0, len(m))
  69. for i, k := range keys {
  70. keys[i] = qualifier + SanitizeLabelName(k)
  71. values = append(values, m[k])
  72. }
  73. return keys, values
  74. }
  75. // Converts kubernetes labels into prometheus labels.
  76. func KubeLabelsToLabels(labels map[string]string) ([]string, []string) {
  77. return KubePrependQualifierToLabels(labels, "label_")
  78. }
  79. // Converts kubernetes annotations into prometheus labels.
  80. func KubeAnnotationsToLabels(labels map[string]string) ([]string, []string) {
  81. return KubePrependQualifierToLabels(labels, "annotation_")
  82. }
  83. // Replaces all illegal prometheus label characters with _
  84. func SanitizeLabelName(s string) string {
  85. return invalidLabelCharRE.ReplaceAllString(s, "_")
  86. }
  87. // SanitizeLabels sanitizes all label names in the given map. This may cause
  88. // collisions, which is intentional as collisions that are not caught prior to
  89. // attempted emission will cause fatal errors. In the case of a collision, the
  90. // last value seen will be set, and all previous values will be overwritten.
  91. func SanitizeLabels(labels map[string]string) map[string]string {
  92. response := make(map[string]string, len(labels))
  93. for k, v := range labels {
  94. response[SanitizeLabelName(k)] = v
  95. }
  96. return response
  97. }