dcgm_test.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package scrape
  2. import (
  3. "testing"
  4. "github.com/opencost/opencost/core/pkg/clustercache"
  5. "github.com/opencost/opencost/core/pkg/source"
  6. "github.com/opencost/opencost/modules/collector-source/pkg/metric"
  7. )
  8. func Test_isDCGM(t *testing.T) {
  9. tests := map[string]struct {
  10. labels map[string]string
  11. want bool
  12. }{
  13. "nil": {
  14. labels: nil,
  15. want: false,
  16. },
  17. "empty": {
  18. labels: map[string]string{},
  19. want: false,
  20. },
  21. "app": {
  22. labels: map[string]string{
  23. "app": "dcgm-exporter",
  24. },
  25. want: true,
  26. },
  27. "app.kubernetes.io/name": {
  28. labels: map[string]string{
  29. "app.kubernetes.io/name": "dcgm-exporter",
  30. },
  31. want: true,
  32. },
  33. "app.kubernetes.io/component": {
  34. labels: map[string]string{
  35. "app.kubernetes.io/name": "dcgm-exporter",
  36. },
  37. want: true,
  38. },
  39. "invalid key": {
  40. labels: map[string]string{
  41. "invalid-key": "dcgm-exporter",
  42. },
  43. want: false,
  44. },
  45. "invalid value": {
  46. labels: map[string]string{
  47. "app.kubernetes.io/name": "dcgmExporter",
  48. },
  49. want: false,
  50. },
  51. "case insensitive": {
  52. labels: map[string]string{
  53. "app.kubernetes.io/name": "jhlkjhlkDcGm-eXpoRterlkjhlkuh",
  54. },
  55. want: true,
  56. },
  57. }
  58. for name, tt := range tests {
  59. t.Run(name, func(t *testing.T) {
  60. if got := isDCGM(tt.labels); got != tt.want {
  61. t.Errorf("isDCGM() = %v, want %v", got, tt.want)
  62. }
  63. })
  64. }
  65. }
  66. func Test_podUIDEnricher(t *testing.T) {
  67. cache := &clustercache.MockClusterCache{
  68. Pods: []*clustercache.Pod{
  69. {UID: "pod-uid-1", Name: "pod1", Namespace: "namespace1"},
  70. {UID: "pod-uid-2", Name: "pod2", Namespace: "namespace2"},
  71. },
  72. }
  73. tests := map[string]struct {
  74. labels map[string]string
  75. want map[string]string
  76. }{
  77. "resolves pod_uid from namespace and pod": {
  78. labels: map[string]string{
  79. source.NamespaceLabel: "namespace1",
  80. source.PodLabel: "pod1",
  81. },
  82. want: map[string]string{
  83. source.NamespaceLabel: "namespace1",
  84. source.PodLabel: "pod1",
  85. source.PodUIDLabel: "pod-uid-1",
  86. },
  87. },
  88. "unknown pod is left unset": {
  89. labels: map[string]string{
  90. source.NamespaceLabel: "namespace1",
  91. source.PodLabel: "unknown-pod",
  92. },
  93. want: map[string]string{
  94. source.NamespaceLabel: "namespace1",
  95. source.PodLabel: "unknown-pod",
  96. },
  97. },
  98. "pod name from wrong namespace is left unset": {
  99. labels: map[string]string{
  100. source.NamespaceLabel: "namespace2",
  101. source.PodLabel: "pod1",
  102. },
  103. want: map[string]string{
  104. source.NamespaceLabel: "namespace2",
  105. source.PodLabel: "pod1",
  106. },
  107. },
  108. "missing namespace label is left unset": {
  109. labels: map[string]string{
  110. source.PodLabel: "pod1",
  111. },
  112. want: map[string]string{
  113. source.PodLabel: "pod1",
  114. },
  115. },
  116. "missing pod label is left unset": {
  117. labels: map[string]string{
  118. source.NamespaceLabel: "namespace1",
  119. },
  120. want: map[string]string{
  121. source.NamespaceLabel: "namespace1",
  122. },
  123. },
  124. "nil labels is left untouched": {
  125. labels: nil,
  126. want: nil,
  127. },
  128. "existing non-empty pod_uid is left untouched": {
  129. labels: map[string]string{
  130. source.NamespaceLabel: "namespace1",
  131. source.PodLabel: "pod1",
  132. source.PodUIDLabel: "already-set",
  133. },
  134. want: map[string]string{
  135. source.NamespaceLabel: "namespace1",
  136. source.PodLabel: "pod1",
  137. source.PodUIDLabel: "already-set",
  138. },
  139. },
  140. }
  141. for name, tt := range tests {
  142. t.Run(name, func(t *testing.T) {
  143. enrich := podUIDEnricher(cache)
  144. updates := []metric.Update{{Labels: cloneLabels(tt.labels)}}
  145. enrich(updates)
  146. assertLabels(t, updates[0].Labels, tt.want)
  147. })
  148. }
  149. // A single call must correctly enrich every update in the batch, not just
  150. // the first, since updates are processed together rather than one at a time.
  151. t.Run("enriches every update in a multi-update batch", func(t *testing.T) {
  152. enrich := podUIDEnricher(cache)
  153. updates := []metric.Update{
  154. {Labels: map[string]string{source.NamespaceLabel: "namespace1", source.PodLabel: "pod1"}},
  155. {Labels: map[string]string{source.NamespaceLabel: "namespace2", source.PodLabel: "pod2"}},
  156. {Labels: map[string]string{source.NamespaceLabel: "namespace1", source.PodLabel: "unknown-pod"}},
  157. }
  158. enrich(updates)
  159. assertLabels(t, updates[0].Labels, map[string]string{
  160. source.NamespaceLabel: "namespace1",
  161. source.PodLabel: "pod1",
  162. source.PodUIDLabel: "pod-uid-1",
  163. })
  164. assertLabels(t, updates[1].Labels, map[string]string{
  165. source.NamespaceLabel: "namespace2",
  166. source.PodLabel: "pod2",
  167. source.PodUIDLabel: "pod-uid-2",
  168. })
  169. assertLabels(t, updates[2].Labels, map[string]string{
  170. source.NamespaceLabel: "namespace1",
  171. source.PodLabel: "unknown-pod",
  172. })
  173. })
  174. t.Run("empty batch does not panic", func(t *testing.T) {
  175. enrich := podUIDEnricher(cache)
  176. enrich(nil)
  177. enrich([]metric.Update{})
  178. })
  179. }
  180. func cloneLabels(labels map[string]string) map[string]string {
  181. if labels == nil {
  182. return nil
  183. }
  184. clone := make(map[string]string, len(labels))
  185. for k, v := range labels {
  186. clone[k] = v
  187. }
  188. return clone
  189. }
  190. func assertLabels(t *testing.T, got, want map[string]string) {
  191. t.Helper()
  192. if len(got) != len(want) {
  193. t.Fatalf("got labels %v, want %v", got, want)
  194. }
  195. for k, v := range want {
  196. if got[k] != v {
  197. t.Errorf("label %q = %q, want %q", k, got[k], v)
  198. }
  199. }
  200. }