Răsfoiți Sursa

Add DEBUG level logging to PrometheusMetricsQuerier and tests

Signed-off-by: Matt Bolt <mbolt35@gmail.com>
Matt Bolt 9 luni în urmă
părinte
comite
19fffabae8

+ 0 - 1
go.mod

@@ -71,7 +71,6 @@ require (
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/prometheus/common v0.63.0 // indirect
 	github.com/sony/gobreaker v0.5.0 // indirect
-	github.com/stretchr/objx v0.5.2 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect

+ 5 - 1
modules/prometheus-source/go.mod

@@ -12,6 +12,7 @@ require (
 	github.com/julienschmidt/httprouter v1.3.0
 	github.com/opencost/opencost/core v0.0.0-20241211165149-ee44b80e2fd0
 	github.com/prometheus/client_golang v1.20.5
+	github.com/rs/zerolog v1.26.1
 	gopkg.in/yaml.v2 v2.4.0
 	k8s.io/client-go v0.33.1
 )
@@ -24,6 +25,8 @@ require (
 	github.com/goccy/go-json v0.10.5 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/google/uuid v1.6.0 // indirect
+	github.com/hashicorp/errwrap v1.0.0 // indirect
+	github.com/hashicorp/go-multierror v1.1.1 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/magiconair/properties v1.8.5 // indirect
@@ -31,10 +34,10 @@ require (
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
 	github.com/pelletier/go-toml v1.9.3 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
 	github.com/prometheus/common v0.63.0 // indirect
-	github.com/rs/zerolog v1.26.1 // indirect
 	github.com/spf13/afero v1.6.0 // indirect
 	github.com/spf13/cast v1.3.1 // indirect
 	github.com/spf13/jwalterweatherman v1.1.0 // indirect
@@ -42,6 +45,7 @@ require (
 	github.com/spf13/viper v1.8.1 // indirect
 	github.com/subosito/gotenv v1.2.0 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
+	golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect
 	golang.org/x/net v0.38.0 // indirect
 	golang.org/x/oauth2 v0.27.0 // indirect
 	golang.org/x/sys v0.31.0 // indirect

+ 7 - 0
modules/prometheus-source/go.sum

@@ -169,11 +169,14 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
 github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
 github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
 github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@@ -240,6 +243,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
+github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
 github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ=
 github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -331,6 +336,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 h1:QfTh0HpN6hlw6D3vu8DAwC8pBIwikq0AI1evdm+FksE=
+golang.org/x/exp v0.0.0-20221031165847-c99f073a8326/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=

Fișier diff suprimat deoarece este prea mare
+ 220 - 130
modules/prometheus-source/pkg/prom/metricsquerier.go


+ 206 - 0
modules/prometheus-source/pkg/prom/metricsquerier_test.go

@@ -0,0 +1,206 @@
+package prom
+
+import (
+	"bytes"
+	"context"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+	zerologger "github.com/rs/zerolog/log"
+)
+
+func initLogging(t *testing.T, logLevel string, colorEnabled bool) {
+	zerolog.TimeFieldFormat = time.RFC3339Nano
+	zerologger.Logger = zerologger.Output(zerolog.ConsoleWriter{
+		Out:        zerolog.NewTestWriter(t),
+		TimeFormat: time.RFC3339Nano,
+		NoColor:    !colorEnabled,
+	})
+
+	logLevelParsed, err := zerolog.ParseLevel(logLevel)
+	if err != nil {
+		logLevelParsed = zerolog.DebugLevel
+	}
+
+	zerolog.SetGlobalLevel(logLevelParsed)
+}
+
+type SingleLogWriter struct {
+	Log string
+}
+
+// Write to testing.TB.
+func (slw *SingleLogWriter) Write(p []byte) (n int, err error) {
+	err = nil
+	n = len(p)
+
+	slw.Log = string(p)
+	return
+}
+
+type NoOpPromClient struct {
+}
+
+func (mpc *NoOpPromClient) URL(ep string, args map[string]string) *url.URL {
+	return &url.URL{}
+}
+func (mpc *NoOpPromClient) Do(c context.Context, req *http.Request) (*http.Response, []byte, error) {
+	return &http.Response{
+			StatusCode: http.StatusOK,
+			Body:       io.NopCloser(bytes.NewReader([]byte{})),
+		},
+		[]byte{},
+		nil
+
+}
+
+func TestQueryLogs(t *testing.T) {
+	// init logging
+	initLogging(t, "debug", false)
+
+	// use a single log writer so we can examine the logs after each query
+	logWriter := new(SingleLogWriter)
+	zerologger.Logger = zerologger.Output(zerolog.ConsoleWriter{
+		Out:        logWriter,
+		TimeFormat: "",
+		NoColor:    true,
+		PartsExclude: []string{
+			zerolog.TimestampFieldName,
+			zerolog.LevelFieldName,
+			zerolog.CallerFieldName,
+		},
+	})
+
+	// reinitialize logging when tests are complete
+	defer initLogging(t, "debug", false)
+
+	t.Setenv("PROMETHEUS_SERVER_ENDPOINT", "nowhere")
+
+	config, err := NewOpenCostPrometheusConfigFromEnv()
+	if err != nil {
+		t.Fatalf("Failed to create OpenCost Prometheus config: %v", err)
+		return
+	}
+
+	mock := new(NoOpPromClient)
+	contextFactory := NewContextFactory(mock, config)
+
+	querier := newPrometheusMetricsQuerier(config, mock, contextFactory)
+
+	queryEnd := time.Now().UTC().Truncate(time.Hour).Add(time.Hour)
+	queryStart := queryEnd.Add(-24 * time.Hour)
+
+	tests := map[string]func(time.Time, time.Time){
+		"QueryPVActiveMinutes":              func(s, e time.Time) { querier.QueryPVActiveMinutes(s, e) },
+		"QueryPVUsedAverage":                func(s, e time.Time) { querier.QueryPVUsedAverage(s, e) },
+		"QueryPVUsedMax":                    func(s, e time.Time) { querier.QueryPVUsedMax(s, e) },
+		"QueryLocalStorageActiveMinutes":    func(s, e time.Time) { querier.QueryLocalStorageActiveMinutes(s, e) },
+		"QueryLocalStorageCost":             func(s, e time.Time) { querier.QueryLocalStorageCost(s, e) },
+		"QueryLocalStorageUsedCost":         func(s, e time.Time) { querier.QueryLocalStorageUsedCost(s, e) },
+		"QueryLocalStorageUsedAvg":          func(s, e time.Time) { querier.QueryLocalStorageUsedAvg(s, e) },
+		"QueryLocalStorageUsedMax":          func(s, e time.Time) { querier.QueryLocalStorageUsedMax(s, e) },
+		"QueryLocalStorageBytes":            func(s, e time.Time) { querier.QueryLocalStorageBytes(s, e) },
+		"QueryNodeActiveMinutes":            func(s, e time.Time) { querier.QueryNodeActiveMinutes(s, e) },
+		"QueryNodeCPUCoresCapacity":         func(s, e time.Time) { querier.QueryNodeCPUCoresCapacity(s, e) },
+		"QueryNodeCPUCoresAllocatable":      func(s, e time.Time) { querier.QueryNodeCPUCoresAllocatable(s, e) },
+		"QueryNodeRAMBytesCapacity":         func(s, e time.Time) { querier.QueryNodeRAMBytesCapacity(s, e) },
+		"QueryNodeRAMBytesAllocatable":      func(s, e time.Time) { querier.QueryNodeRAMBytesAllocatable(s, e) },
+		"QueryNodeGPUCount":                 func(s, e time.Time) { querier.QueryNodeGPUCount(s, e) },
+		"QueryNodeCPUModeTotal":             func(s, e time.Time) { querier.QueryNodeCPUModeTotal(s, e) },
+		"QueryNodeIsSpot":                   func(s, e time.Time) { querier.QueryNodeIsSpot(s, e) },
+		"QueryNodeRAMSystemPercent":         func(s, e time.Time) { querier.QueryNodeRAMSystemPercent(s, e) },
+		"QueryNodeRAMUserPercent":           func(s, e time.Time) { querier.QueryNodeRAMUserPercent(s, e) },
+		"QueryLBActiveMinutes":              func(s, e time.Time) { querier.QueryLBActiveMinutes(s, e) },
+		"QueryLBPricePerHr":                 func(s, e time.Time) { querier.QueryLBPricePerHr(s, e) },
+		"QueryClusterManagementDuration":    func(s, e time.Time) { querier.QueryClusterManagementDuration(s, e) },
+		"QueryClusterManagementPricePerHr":  func(s, e time.Time) { querier.QueryClusterManagementPricePerHr(s, e) },
+		"QueryPods":                         func(s, e time.Time) { querier.QueryPods(s, e) },
+		"QueryPodsUID":                      func(s, e time.Time) { querier.QueryPodsUID(s, e) },
+		"QueryRAMBytesAllocated":            func(s, e time.Time) { querier.QueryRAMBytesAllocated(s, e) },
+		"QueryRAMRequests":                  func(s, e time.Time) { querier.QueryRAMRequests(s, e) },
+		"QueryRAMUsageAvg":                  func(s, e time.Time) { querier.QueryRAMUsageAvg(s, e) },
+		"QueryRAMUsageMax":                  func(s, e time.Time) { querier.QueryRAMUsageMax(s, e) },
+		"QueryNodeRAMPricePerGiBHr":         func(s, e time.Time) { querier.QueryNodeRAMPricePerGiBHr(s, e) },
+		"QueryCPUCoresAllocated":            func(s, e time.Time) { querier.QueryCPUCoresAllocated(s, e) },
+		"QueryCPURequests":                  func(s, e time.Time) { querier.QueryCPURequests(s, e) },
+		"QueryCPUUsageAvg":                  func(s, e time.Time) { querier.QueryCPUUsageAvg(s, e) },
+		"QueryCPUUsageMax":                  func(s, e time.Time) { querier.QueryCPUUsageMax(s, e) },
+		"QueryNodeCPUPricePerHr":            func(s, e time.Time) { querier.QueryNodeCPUPricePerHr(s, e) },
+		"QueryGPUsAllocated":                func(s, e time.Time) { querier.QueryGPUsAllocated(s, e) },
+		"QueryGPUsRequested":                func(s, e time.Time) { querier.QueryGPUsRequested(s, e) },
+		"QueryGPUsUsageAvg":                 func(s, e time.Time) { querier.QueryGPUsUsageAvg(s, e) },
+		"QueryGPUsUsageMax":                 func(s, e time.Time) { querier.QueryGPUsUsageMax(s, e) },
+		"QueryNodeGPUPricePerHr":            func(s, e time.Time) { querier.QueryNodeGPUPricePerHr(s, e) },
+		"QueryGPUInfo":                      func(s, e time.Time) { querier.QueryGPUInfo(s, e) },
+		"QueryIsGPUShared":                  func(s, e time.Time) { querier.QueryIsGPUShared(s, e) },
+		"QueryPodPVCAllocation":             func(s, e time.Time) { querier.QueryPodPVCAllocation(s, e) },
+		"QueryPVCBytesRequested":            func(s, e time.Time) { querier.QueryPVCBytesRequested(s, e) },
+		"QueryPVCInfo":                      func(s, e time.Time) { querier.QueryPVCInfo(s, e) },
+		"QueryPVBytes":                      func(s, e time.Time) { querier.QueryPVBytes(s, e) },
+		"QueryPVPricePerGiBHour":            func(s, e time.Time) { querier.QueryPVPricePerGiBHour(s, e) },
+		"QueryPVInfo":                       func(s, e time.Time) { querier.QueryPVInfo(s, e) },
+		"QueryNetZoneGiB":                   func(s, e time.Time) { querier.QueryNetZoneGiB(s, e) },
+		"QueryNetZonePricePerGiB":           func(s, e time.Time) { querier.QueryNetZonePricePerGiB(s, e) },
+		"QueryNetRegionGiB":                 func(s, e time.Time) { querier.QueryNetRegionGiB(s, e) },
+		"QueryNetRegionPricePerGiB":         func(s, e time.Time) { querier.QueryNetRegionPricePerGiB(s, e) },
+		"QueryNetInternetGiB":               func(s, e time.Time) { querier.QueryNetInternetGiB(s, e) },
+		"QueryNetInternetPricePerGiB":       func(s, e time.Time) { querier.QueryNetInternetPricePerGiB(s, e) },
+		"QueryNetInternetServiceGiB":        func(s, e time.Time) { querier.QueryNetInternetServiceGiB(s, e) },
+		"QueryNetTransferBytes":             func(s, e time.Time) { querier.QueryNetTransferBytes(s, e) },
+		"QueryNetZoneIngressGiB":            func(s, e time.Time) { querier.QueryNetZoneIngressGiB(s, e) },
+		"QueryNetRegionIngressGiB":          func(s, e time.Time) { querier.QueryNetRegionIngressGiB(s, e) },
+		"QueryNetInternetIngressGiB":        func(s, e time.Time) { querier.QueryNetInternetIngressGiB(s, e) },
+		"QueryNetInternetServiceIngressGiB": func(s, e time.Time) { querier.QueryNetInternetServiceIngressGiB(s, e) },
+		"QueryNetReceiveBytes":              func(s, e time.Time) { querier.QueryNetReceiveBytes(s, e) },
+		"QueryNamespaceAnnotations":         func(s, e time.Time) { querier.QueryNamespaceAnnotations(s, e) },
+		"QueryPodAnnotations":               func(s, e time.Time) { querier.QueryPodAnnotations(s, e) },
+		"QueryNodeLabels":                   func(s, e time.Time) { querier.QueryNodeLabels(s, e) },
+		"QueryNamespaceLabels":              func(s, e time.Time) { querier.QueryNamespaceLabels(s, e) },
+		"QueryPodLabels":                    func(s, e time.Time) { querier.QueryPodLabels(s, e) },
+		"QueryServiceLabels":                func(s, e time.Time) { querier.QueryServiceLabels(s, e) },
+		"QueryDeploymentLabels":             func(s, e time.Time) { querier.QueryDeploymentLabels(s, e) },
+		"QueryStatefulSetLabels":            func(s, e time.Time) { querier.QueryStatefulSetLabels(s, e) },
+		"QueryDaemonSetLabels":              func(s, e time.Time) { querier.QueryDaemonSetLabels(s, e) },
+		"QueryJobLabels":                    func(s, e time.Time) { querier.QueryJobLabels(s, e) },
+		"QueryPodsWithReplicaSetOwner":      func(s, e time.Time) { querier.QueryPodsWithReplicaSetOwner(s, e) },
+		"QueryReplicaSetsWithoutOwners":     func(s, e time.Time) { querier.QueryReplicaSetsWithoutOwners(s, e) },
+		"QueryReplicaSetsWithRollout":       func(s, e time.Time) { querier.QueryReplicaSetsWithRollout(s, e) },
+	}
+
+	for testName, queryFunc := range tests {
+		t.Run(fmt.Sprintf("TestQueryLog_%s", testName), func(t *testing.T) {
+			checkQueryLog(t, logWriter, queryFunc, testName, queryStart, queryEnd)
+		})
+	}
+}
+
+func checkQueryLog(t *testing.T, logWriter *SingleLogWriter, query func(time.Time, time.Time), queryName string, start time.Time, end time.Time) {
+	t.Helper()
+
+	// remove the query formatting for the log
+	var headerFormat = PrometheusMetricsQueryLogFormat[:len(PrometheusMetricsQueryLogFormat)-3]
+
+	query(start, end)
+
+	// get log output from executing query
+	output := logWriter.Log
+
+	expectedHeader := fmt.Sprintf(headerFormat, queryName, end.Unix())
+	headerLen := len(expectedHeader)
+
+	if len(output) < headerLen {
+		t.Errorf("Expected log header length %d, but got %d", headerLen, len(output))
+		return
+	}
+
+	actual := output[:headerLen]
+	if actual != expectedHeader {
+		t.Errorf("Expected log header '%s', but got '%s'", expectedHeader, actual)
+		return
+	}
+}

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff