decoders_test.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package source
  2. import (
  3. "testing"
  4. "github.com/opencost/opencost/core/pkg/util"
  5. )
  6. func TestDecodeGPUSaturationResult(t *testing.T) {
  7. values := []*util.Vector{{Timestamp: 1000, Value: 0.25}}
  8. t.Run("all labels present", func(t *testing.T) {
  9. result := NewQueryResult(map[string]any{
  10. "pod_uid": "pod-uid-1",
  11. "cluster_id": "cluster-1",
  12. "namespace": "gpu-ns",
  13. "pod": "gpu-pod",
  14. "container": "gpu-container",
  15. "device": "nvidia0",
  16. "modelName": "NVIDIA A100-SXM4-40GB",
  17. "UUID": "GPU-1234",
  18. "GPU_I_PROFILE": "1g.5gb",
  19. "GPU_I_ID": "3",
  20. "reason": "sw_power_cap",
  21. }, values, nil)
  22. decoded := DecodeGPUSaturationResult(result)
  23. if decoded.UID != "pod-uid-1" {
  24. t.Errorf("UID = %q, want %q", decoded.UID, "pod-uid-1")
  25. }
  26. if decoded.Cluster != "cluster-1" {
  27. t.Errorf("Cluster = %q, want %q", decoded.Cluster, "cluster-1")
  28. }
  29. if decoded.Namespace != "gpu-ns" {
  30. t.Errorf("Namespace = %q, want %q", decoded.Namespace, "gpu-ns")
  31. }
  32. if decoded.Pod != "gpu-pod" {
  33. t.Errorf("Pod = %q, want %q", decoded.Pod, "gpu-pod")
  34. }
  35. if decoded.Container != "gpu-container" {
  36. t.Errorf("Container = %q, want %q", decoded.Container, "gpu-container")
  37. }
  38. if decoded.Device != "nvidia0" {
  39. t.Errorf("Device = %q, want %q", decoded.Device, "nvidia0")
  40. }
  41. if decoded.ModelName != "NVIDIA A100-SXM4-40GB" {
  42. t.Errorf("ModelName = %q, want %q", decoded.ModelName, "NVIDIA A100-SXM4-40GB")
  43. }
  44. if decoded.UUID != "GPU-1234" {
  45. t.Errorf("UUID = %q, want %q", decoded.UUID, "GPU-1234")
  46. }
  47. if decoded.MIGProfile != "1g.5gb" {
  48. t.Errorf("MIGProfile = %q, want %q", decoded.MIGProfile, "1g.5gb")
  49. }
  50. if decoded.MIGInstance != "3" {
  51. t.Errorf("MIGInstance = %q, want %q", decoded.MIGInstance, "3")
  52. }
  53. if decoded.Reason != "sw_power_cap" {
  54. t.Errorf("Reason = %q, want %q", decoded.Reason, "sw_power_cap")
  55. }
  56. if len(decoded.Data) != 1 || decoded.Data[0].Value != 0.25 {
  57. t.Errorf("Data not passed through: %+v", decoded.Data)
  58. }
  59. })
  60. t.Run("optional labels absent", func(t *testing.T) {
  61. // non-MIG GPU without a reason-labeled query: those labels simply
  62. // do not exist on the series and must decode to empty strings
  63. // legacy scrape configs label the pod UID as "uid"; the decoder
  64. // falls back to it when pod_uid is absent
  65. result := NewQueryResult(map[string]any{
  66. "uid": "pod-uid-1",
  67. "cluster_id": "cluster-1",
  68. "namespace": "gpu-ns",
  69. "pod": "gpu-pod",
  70. "container": "gpu-container",
  71. "UUID": "GPU-1234",
  72. }, values, nil)
  73. decoded := DecodeGPUSaturationResult(result)
  74. if decoded.MIGProfile != "" || decoded.MIGInstance != "" || decoded.Reason != "" {
  75. t.Errorf("expected absent labels to decode to empty strings, got %+v", decoded)
  76. }
  77. if decoded.Device != "" || decoded.ModelName != "" {
  78. t.Errorf("expected absent device labels to decode to empty strings, got %+v", decoded)
  79. }
  80. if decoded.UUID != "GPU-1234" {
  81. t.Errorf("UUID = %q, want %q", decoded.UUID, "GPU-1234")
  82. }
  83. })
  84. }