metricsquerier_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. package prom
  2. import (
  3. "bytes"
  4. "context"
  5. "fmt"
  6. "io"
  7. "net/http"
  8. "net/url"
  9. "testing"
  10. "time"
  11. "github.com/rs/zerolog"
  12. zerologger "github.com/rs/zerolog/log"
  13. )
  14. func initLogging(t *testing.T, logLevel string, colorEnabled bool) {
  15. zerolog.TimeFieldFormat = time.RFC3339Nano
  16. zerologger.Logger = zerologger.Output(zerolog.ConsoleWriter{
  17. Out: zerolog.NewTestWriter(t),
  18. TimeFormat: time.RFC3339Nano,
  19. NoColor: !colorEnabled,
  20. })
  21. logLevelParsed, err := zerolog.ParseLevel(logLevel)
  22. if err != nil {
  23. logLevelParsed = zerolog.DebugLevel
  24. }
  25. zerolog.SetGlobalLevel(logLevelParsed)
  26. }
  27. type SingleLogWriter struct {
  28. Log string
  29. }
  30. // Write to testing.TB.
  31. func (slw *SingleLogWriter) Write(p []byte) (n int, err error) {
  32. err = nil
  33. n = len(p)
  34. slw.Log = string(p)
  35. return
  36. }
  37. type NoOpPromClient struct {
  38. }
  39. func (mpc *NoOpPromClient) URL(ep string, args map[string]string) *url.URL {
  40. return &url.URL{}
  41. }
  42. func (mpc *NoOpPromClient) Do(c context.Context, req *http.Request) (*http.Response, []byte, error) {
  43. return &http.Response{
  44. StatusCode: http.StatusOK,
  45. Body: io.NopCloser(bytes.NewReader([]byte{})),
  46. },
  47. []byte{},
  48. nil
  49. }
  50. func TestQueryLogs(t *testing.T) {
  51. // init logging
  52. initLogging(t, "debug", false)
  53. // use a single log writer so we can examine the logs after each query
  54. logWriter := new(SingleLogWriter)
  55. zerologger.Logger = zerologger.Output(zerolog.ConsoleWriter{
  56. Out: logWriter,
  57. TimeFormat: "",
  58. NoColor: true,
  59. PartsExclude: []string{
  60. zerolog.TimestampFieldName,
  61. zerolog.LevelFieldName,
  62. zerolog.CallerFieldName,
  63. },
  64. })
  65. // reinitialize logging when tests are complete
  66. defer initLogging(t, "debug", false)
  67. t.Setenv("PROMETHEUS_SERVER_ENDPOINT", "nowhere")
  68. config, err := NewOpenCostPrometheusConfigFromEnv()
  69. if err != nil {
  70. t.Fatalf("Failed to create OpenCost Prometheus config: %v", err)
  71. return
  72. }
  73. mock := new(NoOpPromClient)
  74. contextFactory := NewContextFactory(mock, config)
  75. querier := newPrometheusMetricsQuerier(config, mock, contextFactory)
  76. queryEnd := time.Now().UTC().Truncate(time.Hour).Add(time.Hour)
  77. queryStart := queryEnd.Add(-24 * time.Hour)
  78. tests := map[string]func(time.Time, time.Time){
  79. "QueryPVActiveMinutes": func(s, e time.Time) { querier.QueryPVActiveMinutes(s, e) },
  80. "QueryPVUsedAverage": func(s, e time.Time) { querier.QueryPVUsedAverage(s, e) },
  81. "QueryPVUsedMax": func(s, e time.Time) { querier.QueryPVUsedMax(s, e) },
  82. "QueryLocalStorageActiveMinutes": func(s, e time.Time) { querier.QueryLocalStorageActiveMinutes(s, e) },
  83. "QueryLocalStorageUsedAvg": func(s, e time.Time) { querier.QueryLocalStorageUsedAvg(s, e) },
  84. "QueryLocalStorageUsedMax": func(s, e time.Time) { querier.QueryLocalStorageUsedMax(s, e) },
  85. "QueryLocalStorageBytes": func(s, e time.Time) { querier.QueryLocalStorageBytes(s, e) },
  86. "QueryNodeActiveMinutes": func(s, e time.Time) { querier.QueryNodeActiveMinutes(s, e) },
  87. "QueryNodeCPUCoresCapacity": func(s, e time.Time) { querier.QueryNodeCPUCoresCapacity(s, e) },
  88. "QueryNodeCPUCoresAllocatable": func(s, e time.Time) { querier.QueryNodeCPUCoresAllocatable(s, e) },
  89. "QueryNodeRAMBytesCapacity": func(s, e time.Time) { querier.QueryNodeRAMBytesCapacity(s, e) },
  90. "QueryNodeRAMBytesAllocatable": func(s, e time.Time) { querier.QueryNodeRAMBytesAllocatable(s, e) },
  91. "QueryNodeGPUCount": func(s, e time.Time) { querier.QueryNodeGPUCount(s, e) },
  92. "QueryNodeCPUModeTotal": func(s, e time.Time) { querier.QueryNodeCPUModeTotal(s, e) },
  93. "QueryNodeIsSpot": func(s, e time.Time) { querier.QueryNodeIsSpot(s, e) },
  94. "QueryNodeRAMSystemPercent": func(s, e time.Time) { querier.QueryNodeRAMSystemPercent(s, e) },
  95. "QueryNodeRAMUserPercent": func(s, e time.Time) { querier.QueryNodeRAMUserPercent(s, e) },
  96. "QueryLBActiveMinutes": func(s, e time.Time) { querier.QueryLBActiveMinutes(s, e) },
  97. "QueryLBPricePerHr": func(s, e time.Time) { querier.QueryLBPricePerHr(s, e) },
  98. "QueryClusterManagementDuration": func(s, e time.Time) { querier.QueryClusterManagementDuration(s, e) },
  99. "QueryClusterManagementPricePerHr": func(s, e time.Time) { querier.QueryClusterManagementPricePerHr(s, e) },
  100. "QueryPods": func(s, e time.Time) { querier.QueryPods(s, e) },
  101. "QueryPodsUID": func(s, e time.Time) { querier.QueryPodsUID(s, e) },
  102. "QueryRAMBytesAllocated": func(s, e time.Time) { querier.QueryRAMBytesAllocated(s, e) },
  103. "QueryRAMRequests": func(s, e time.Time) { querier.QueryRAMRequests(s, e) },
  104. "QueryRAMLimits": func(s, e time.Time) { querier.QueryRAMLimits(s, e) },
  105. "QueryRAMUsageAvg": func(s, e time.Time) { querier.QueryRAMUsageAvg(s, e) },
  106. "QueryRAMUsageMax": func(s, e time.Time) { querier.QueryRAMUsageMax(s, e) },
  107. "QueryNodeRAMPricePerGiBHr": func(s, e time.Time) { querier.QueryNodeRAMPricePerGiBHr(s, e) },
  108. "QueryCPUCoresAllocated": func(s, e time.Time) { querier.QueryCPUCoresAllocated(s, e) },
  109. "QueryCPURequests": func(s, e time.Time) { querier.QueryCPURequests(s, e) },
  110. "QueryCPULimits": func(s, e time.Time) { querier.QueryCPULimits(s, e) },
  111. "QueryCPUUsageAvg": func(s, e time.Time) { querier.QueryCPUUsageAvg(s, e) },
  112. "QueryCPUUsageMax": func(s, e time.Time) { querier.QueryCPUUsageMax(s, e) },
  113. "QueryNodeCPUPricePerHr": func(s, e time.Time) { querier.QueryNodeCPUPricePerHr(s, e) },
  114. "QueryGPUsAllocated": func(s, e time.Time) { querier.QueryGPUsAllocated(s, e) },
  115. "QueryGPUsRequested": func(s, e time.Time) { querier.QueryGPUsRequested(s, e) },
  116. "QueryGPUsUsageAvg": func(s, e time.Time) { querier.QueryGPUsUsageAvg(s, e) },
  117. "QueryGPUsUsageMax": func(s, e time.Time) { querier.QueryGPUsUsageMax(s, e) },
  118. "QueryNodeGPUPricePerHr": func(s, e time.Time) { querier.QueryNodeGPUPricePerHr(s, e) },
  119. "QueryGPUInfo": func(s, e time.Time) { querier.QueryGPUInfo(s, e) },
  120. "QueryIsGPUShared": func(s, e time.Time) { querier.QueryIsGPUShared(s, e) },
  121. "QueryPodPVCAllocation": func(s, e time.Time) { querier.QueryPodPVCAllocation(s, e) },
  122. "QueryPVCBytesRequested": func(s, e time.Time) { querier.QueryPVCBytesRequested(s, e) },
  123. "QueryPVCInfo": func(s, e time.Time) { querier.QueryPVCInfo(s, e) },
  124. "QueryPVBytes": func(s, e time.Time) { querier.QueryPVBytes(s, e) },
  125. "QueryPVPricePerGiBHour": func(s, e time.Time) { querier.QueryPVPricePerGiBHour(s, e) },
  126. "QueryPVInfo": func(s, e time.Time) { querier.QueryPVInfo(s, e) },
  127. "QueryNetZoneGiB": func(s, e time.Time) { querier.QueryNetZoneGiB(s, e) },
  128. "QueryNetZonePricePerGiB": func(s, e time.Time) { querier.QueryNetZonePricePerGiB(s, e) },
  129. "QueryNetRegionGiB": func(s, e time.Time) { querier.QueryNetRegionGiB(s, e) },
  130. "QueryNetRegionPricePerGiB": func(s, e time.Time) { querier.QueryNetRegionPricePerGiB(s, e) },
  131. "QueryNetInternetGiB": func(s, e time.Time) { querier.QueryNetInternetGiB(s, e) },
  132. "QueryNetInternetPricePerGiB": func(s, e time.Time) { querier.QueryNetInternetPricePerGiB(s, e) },
  133. "QueryNetInternetServiceGiB": func(s, e time.Time) { querier.QueryNetInternetServiceGiB(s, e) },
  134. "QueryNetTransferBytes": func(s, e time.Time) { querier.QueryNetTransferBytes(s, e) },
  135. "QueryNetZoneIngressGiB": func(s, e time.Time) { querier.QueryNetZoneIngressGiB(s, e) },
  136. "QueryNetRegionIngressGiB": func(s, e time.Time) { querier.QueryNetRegionIngressGiB(s, e) },
  137. "QueryNetInternetIngressGiB": func(s, e time.Time) { querier.QueryNetInternetIngressGiB(s, e) },
  138. "QueryNetInternetServiceIngressGiB": func(s, e time.Time) { querier.QueryNetInternetServiceIngressGiB(s, e) },
  139. "QueryNetReceiveBytes": func(s, e time.Time) { querier.QueryNetReceiveBytes(s, e) },
  140. "QueryNamespaceAnnotations": func(s, e time.Time) { querier.QueryNamespaceAnnotations(s, e) },
  141. "QueryPodAnnotations": func(s, e time.Time) { querier.QueryPodAnnotations(s, e) },
  142. "QueryNodeLabels": func(s, e time.Time) { querier.QueryNodeLabels(s, e) },
  143. "QueryNamespaceLabels": func(s, e time.Time) { querier.QueryNamespaceLabels(s, e) },
  144. "QueryPodLabels": func(s, e time.Time) { querier.QueryPodLabels(s, e) },
  145. "QueryServiceLabels": func(s, e time.Time) { querier.QueryServiceLabels(s, e) },
  146. "QueryDeploymentLabels": func(s, e time.Time) { querier.QueryDeploymentLabels(s, e) },
  147. "QueryStatefulSetLabels": func(s, e time.Time) { querier.QueryStatefulSetLabels(s, e) },
  148. "QueryDaemonSetLabels": func(s, e time.Time) { querier.QueryDaemonSetLabels(s, e) },
  149. "QueryJobLabels": func(s, e time.Time) { querier.QueryJobLabels(s, e) },
  150. "QueryPodsWithReplicaSetOwner": func(s, e time.Time) { querier.QueryPodsWithReplicaSetOwner(s, e) },
  151. "QueryReplicaSetsWithoutOwners": func(s, e time.Time) { querier.QueryReplicaSetsWithoutOwners(s, e) },
  152. "QueryReplicaSetsWithRollout": func(s, e time.Time) { querier.QueryReplicaSetsWithRollout(s, e) },
  153. "QueryResourceQuotaSpecCPURequestAverage": func(s, e time.Time) { querier.QueryResourceQuotaSpecCPURequestAverage(s, e) },
  154. "QueryResourceQuotaSpecCPURequestMax": func(s, e time.Time) { querier.QueryResourceQuotaSpecCPURequestMax(s, e) },
  155. "QueryResourceQuotaSpecRAMRequestAverage": func(s, e time.Time) { querier.QueryResourceQuotaSpecRAMRequestAverage(s, e) },
  156. "QueryResourceQuotaSpecRAMRequestMax": func(s, e time.Time) { querier.QueryResourceQuotaSpecRAMRequestMax(s, e) },
  157. "QueryResourceQuotaSpecCPULimitAverage": func(s, e time.Time) { querier.QueryResourceQuotaSpecCPULimitAverage(s, e) },
  158. "QueryResourceQuotaSpecCPULimitMax": func(s, e time.Time) { querier.QueryResourceQuotaSpecCPULimitMax(s, e) },
  159. "QueryResourceQuotaSpecRAMLimitAverage": func(s, e time.Time) { querier.QueryResourceQuotaSpecRAMLimitAverage(s, e) },
  160. "QueryResourceQuotaSpecRAMLimitMax": func(s, e time.Time) { querier.QueryResourceQuotaSpecRAMLimitMax(s, e) },
  161. "QueryResourceQuotaStatusUsedCPURequestAverage": func(s, e time.Time) { querier.QueryResourceQuotaStatusUsedCPURequestAverage(s, e) },
  162. "QueryResourceQuotaStatusUsedCPURequestMax": func(s, e time.Time) { querier.QueryResourceQuotaStatusUsedCPURequestMax(s, e) },
  163. "QueryResourceQuotaStatusUsedRAMRequestAverage": func(s, e time.Time) { querier.QueryResourceQuotaStatusUsedRAMRequestAverage(s, e) },
  164. "QueryResourceQuotaStatusUsedRAMRequestMax": func(s, e time.Time) { querier.QueryResourceQuotaStatusUsedRAMRequestMax(s, e) },
  165. "QueryResourceQuotaStatusUsedCPULimitAverage": func(s, e time.Time) { querier.QueryResourceQuotaStatusUsedCPULimitAverage(s, e) },
  166. "QueryResourceQuotaStatusUsedCPULimitMax": func(s, e time.Time) { querier.QueryResourceQuotaStatusUsedCPULimitMax(s, e) },
  167. "QueryResourceQuotaStatusUsedRAMLimitAverage": func(s, e time.Time) { querier.QueryResourceQuotaStatusUsedRAMLimitAverage(s, e) },
  168. "QueryResourceQuotaStatusUsedRAMLimitMax": func(s, e time.Time) { querier.QueryResourceQuotaStatusUsedRAMLimitMax(s, e) },
  169. }
  170. for testName, queryFunc := range tests {
  171. t.Run(fmt.Sprintf("TestQueryLog_%s", testName), func(t *testing.T) {
  172. checkQueryLog(t, logWriter, queryFunc, testName, queryStart, queryEnd)
  173. })
  174. }
  175. }
  176. func checkQueryLog(t *testing.T, logWriter *SingleLogWriter, query func(time.Time, time.Time), queryName string, start time.Time, end time.Time) {
  177. t.Helper()
  178. // remove the query formatting for the log
  179. var headerFormat = PrometheusMetricsQueryLogFormat[:len(PrometheusMetricsQueryLogFormat)-3]
  180. query(start, end)
  181. // get log output from executing query
  182. output := logWriter.Log
  183. expectedHeader := fmt.Sprintf(headerFormat, queryName, end.Unix())
  184. headerLen := len(expectedHeader)
  185. if len(output) < headerLen {
  186. t.Errorf("Expected log header length %d, but got %d", headerLen, len(output))
  187. return
  188. }
  189. actual := output[:headerLen]
  190. if actual != expectedHeader {
  191. t.Errorf("Expected log header '%s', but got '%s'", expectedHeader, actual)
  192. return
  193. }
  194. }