promutil.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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. // sanitize the keys in m to prevent duplicate output keys
  64. sanitizedM := make(map[string]string)
  65. for k, v := range m {
  66. sanitizedM[SanitizeLabelName(k)] = v
  67. }
  68. keys := make([]string, 0, len(sanitizedM))
  69. for k := range sanitizedM {
  70. keys = append(keys, k)
  71. }
  72. sort.Strings(keys)
  73. values := make([]string, 0, len(sanitizedM))
  74. for i, k := range keys {
  75. keys[i] = qualifier + k
  76. values = append(values, sanitizedM[k])
  77. }
  78. return keys, values
  79. }
  80. // Converts kubernetes labels into prometheus labels.
  81. func KubeLabelsToLabels(labels map[string]string) ([]string, []string) {
  82. return KubePrependQualifierToLabels(labels, "label_")
  83. }
  84. // Converts kubernetes annotations into prometheus labels.
  85. func KubeAnnotationsToLabels(labels map[string]string) ([]string, []string) {
  86. return KubePrependQualifierToLabels(labels, "annotation_")
  87. }
  88. // Replaces all illegal prometheus label characters with _
  89. func SanitizeLabelName(s string) string {
  90. return invalidLabelCharRE.ReplaceAllString(s, "_")
  91. }
  92. // SanitizeLabels sanitizes all label names in the given map. This may cause
  93. // collisions, which is intentional as collisions that are not caught prior to
  94. // attempted emission will cause fatal errors. In the case of a collision, the
  95. // last value seen will be set, and all previous values will be overwritten.
  96. func SanitizeLabels(labels map[string]string) map[string]string {
  97. response := make(map[string]string, len(labels))
  98. for k, v := range labels {
  99. response[SanitizeLabelName(k)] = v
  100. }
  101. return response
  102. }