From 5949c329a5064b520ba521da9e1bc032945f8f49 Mon Sep 17 00:00:00 2001 From: Pedro Tanaka Date: Thu, 2 May 2024 23:30:00 +0200 Subject: [PATCH 1/8] fixing extended functions support in more places Signed-off-by: Pedro Tanaka --- cmd/thanos/rule.go | 3 ++- internal/cortex/querier/queryrange/results_cache.go | 5 +++-- internal/cortex/querier/queryrange/split_by_interval.go | 2 +- .../cortex/querier/queryrange/split_by_interval_test.go | 9 +++++++-- pkg/exemplars/multitsdb.go | 3 ++- pkg/exemplars/proxy.go | 2 +- pkg/exemplars/proxy_test.go | 3 ++- pkg/queryfrontend/queryinstant_codec.go | 3 ++- pkg/querysharding/analyzer.go | 3 ++- 9 files changed, 22 insertions(+), 11 deletions(-) diff --git a/cmd/thanos/rule.go b/cmd/thanos/rule.go index 4b89989bac..3523c5d5c5 100644 --- a/cmd/thanos/rule.go +++ b/cmd/thanos/rule.go @@ -59,6 +59,7 @@ import ( "github.com/thanos-io/thanos/pkg/extkingpin" "github.com/thanos-io/thanos/pkg/extprom" extpromhttp "github.com/thanos-io/thanos/pkg/extprom/http" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/info" "github.com/thanos-io/thanos/pkg/info/infopb" "github.com/thanos-io/thanos/pkg/logging" @@ -951,7 +952,7 @@ func queryFuncCreator( queryAPIClients := grpcEndpointSet.GetQueryAPIClients() for _, i := range rand.Perm(len(queryAPIClients)) { e := query.NewRemoteEngine(logger, queryAPIClients[i], query.Opts{}) - expr, err := parser.ParseExpr(qs) + expr, err := extpromql.ParserExpr(qs) if err != nil { level.Error(logger).Log("err", err, "query", qs) continue diff --git a/internal/cortex/querier/queryrange/results_cache.go b/internal/cortex/querier/queryrange/results_cache.go index ae012200f6..2e0a16d9b0 100644 --- a/internal/cortex/querier/queryrange/results_cache.go +++ b/internal/cortex/querier/queryrange/results_cache.go @@ -7,6 +7,7 @@ import ( "context" "flag" "fmt" + "github.com/thanos-io/thanos/pkg/extpromql" "net/http" "sort" "strings" @@ -325,7 +326,7 @@ func (s resultsCache) isAtModifierCachable(r Request, maxCacheTime int64) bool { if !strings.Contains(query, "@") { return true } - expr, err := parser.ParseExpr(query) + expr, err := extpromql.ParserExpr(query) if err != nil { // We are being pessimistic in such cases. level.Warn(s.logger).Log("msg", "failed to parse query, considering @ modifier as not cachable", "query", query, "err", err) @@ -370,7 +371,7 @@ func (s resultsCache) isOffsetCachable(r Request) bool { if !strings.Contains(query, "offset") { return true } - expr, err := parser.ParseExpr(query) + expr, err := extpromql.ParserExpr(query) if err != nil { level.Warn(s.logger).Log("msg", "failed to parse query, considering offset as not cachable", "query", query, "err", err) return false diff --git a/internal/cortex/querier/queryrange/split_by_interval.go b/internal/cortex/querier/queryrange/split_by_interval.go index 039306e880..cb142d07ed 100644 --- a/internal/cortex/querier/queryrange/split_by_interval.go +++ b/internal/cortex/querier/queryrange/split_by_interval.go @@ -97,7 +97,7 @@ func splitQuery(r Request, interval time.Duration) ([]Request, error) { // For example given the start of the query is 10.00, `http_requests_total[1h] @ start()` query will be replaced with `http_requests_total[1h] @ 10.00` // If the modifier is already a constant, it will be returned as is. func EvaluateAtModifierFunction(query string, start, end int64) (string, error) { - expr, err := parser.ParseExpr(query) + expr, err := extpromql.ParserExpr(query) if err != nil { return "", httpgrpc.Errorf(http.StatusBadRequest, `{"status": "error", "error": "%s"}`, err) } diff --git a/internal/cortex/querier/queryrange/split_by_interval_test.go b/internal/cortex/querier/queryrange/split_by_interval_test.go index bd0e96b35d..369f4d71b5 100644 --- a/internal/cortex/querier/queryrange/split_by_interval_test.go +++ b/internal/cortex/querier/queryrange/split_by_interval_test.go @@ -5,6 +5,7 @@ package queryrange import ( "context" + "github.com/thanos-io/thanos/pkg/extpromql" io "io" "net/http" "net/http/httptest" @@ -13,7 +14,6 @@ import ( "testing" "time" - "github.com/prometheus/prometheus/promql/parser" "github.com/stretchr/testify/require" "github.com/weaveworks/common/httpgrpc" "github.com/weaveworks/common/middleware" @@ -332,6 +332,11 @@ func Test_evaluateAtModifier(t *testing.T) { in: "topk(5, rate(http_requests_total[1h] @ start()))", expected: "topk(5, rate(http_requests_total[1h] @ 1546300.800))", }, + { + // extended functions + in: "topk(5, xrate(http_requests_total[1h] @ start()))", + expected: "topk(5, xrate(http_requests_total[1h] @ 1546300.800))", + }, { in: "topk(5, rate(http_requests_total[1h] @ 0))", expected: "topk(5, rate(http_requests_total[1h] @ 0.000))", @@ -390,7 +395,7 @@ func Test_evaluateAtModifier(t *testing.T) { require.Equal(t, tt.expectedErrorCode, int(httpResp.Code)) } else { require.NoError(t, err) - expectedExpr, err := parser.ParseExpr(tt.expected) + expectedExpr, err := extpromql.ParserExpr(tt.expected) require.NoError(t, err) require.Equal(t, expectedExpr.String(), out) } diff --git a/pkg/exemplars/multitsdb.go b/pkg/exemplars/multitsdb.go index c70811b8db..ff1442ae8f 100644 --- a/pkg/exemplars/multitsdb.go +++ b/pkg/exemplars/multitsdb.go @@ -6,6 +6,7 @@ package exemplars import ( "github.com/pkg/errors" "github.com/prometheus/prometheus/promql/parser" + "github.com/thanos-io/thanos/pkg/extpromql" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -26,7 +27,7 @@ func NewMultiTSDB(tsdbExemplarsServers func() map[string]*TSDB) *MultiTSDB { // Exemplars returns all specified exemplars from a MultiTSDB instance. func (m *MultiTSDB) Exemplars(r *exemplarspb.ExemplarsRequest, s exemplarspb.Exemplars_ExemplarsServer) error { - expr, err := parser.ParseExpr(r.Query) + expr, err := extpromql.ParserExpr(r.Query) if err != nil { return status.Error(codes.Internal, err.Error()) } diff --git a/pkg/exemplars/proxy.go b/pkg/exemplars/proxy.go index 052f7922ac..510914fadb 100644 --- a/pkg/exemplars/proxy.go +++ b/pkg/exemplars/proxy.go @@ -59,7 +59,7 @@ func (s *Proxy) Exemplars(req *exemplarspb.ExemplarsRequest, srv exemplarspb.Exe span, ctx := tracing.StartSpan(srv.Context(), "proxy_exemplars") defer span.Finish() - expr, err := parser.ParseExpr(req.Query) + expr, err := extpromql.ParserExpr(req.Query) if err != nil { return err } diff --git a/pkg/exemplars/proxy_test.go b/pkg/exemplars/proxy_test.go index 16c1724f27..f1cb19001c 100644 --- a/pkg/exemplars/proxy_test.go +++ b/pkg/exemplars/proxy_test.go @@ -6,6 +6,7 @@ package exemplars import ( "context" "fmt" + "github.com/thanos-io/thanos/pkg/extpromql" "io" "os" "reflect" @@ -54,7 +55,7 @@ func (t *testExemplarClient) Recv() (*exemplarspb.ExemplarsResponse, error) { } func (t *testExemplarClient) Exemplars(ctx context.Context, in *exemplarspb.ExemplarsRequest, opts ...grpc.CallOption) (exemplarspb.Exemplars_ExemplarsClient, error) { - expr, err := parser.ParseExpr(in.Query) + expr, err := extpromql.ParserExpr(in.Query) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } diff --git a/pkg/queryfrontend/queryinstant_codec.go b/pkg/queryfrontend/queryinstant_codec.go index edd412a4a9..ff3c0b1885 100644 --- a/pkg/queryfrontend/queryinstant_codec.go +++ b/pkg/queryfrontend/queryinstant_codec.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "encoding/json" + "github.com/thanos-io/thanos/pkg/extpromql" "io" "net/http" "net/url" @@ -370,7 +371,7 @@ const ( ) func sortPlanForQuery(q string) (sortPlan, error) { - expr, err := parser.ParseExpr(q) + expr, err := extpromql.ParserExpr(q) if err != nil { return 0, err } diff --git a/pkg/querysharding/analyzer.go b/pkg/querysharding/analyzer.go index 7b8e849bca..c6f7e372e5 100644 --- a/pkg/querysharding/analyzer.go +++ b/pkg/querysharding/analyzer.go @@ -18,6 +18,7 @@ package querysharding import ( "fmt" + "github.com/thanos-io/thanos/pkg/extpromql" lru "github.com/hashicorp/golang-lru/v2" "github.com/prometheus/common/model" @@ -88,7 +89,7 @@ func (a *CachedQueryAnalyzer) Analyze(query string) (QueryAnalysis, error) { // // The le label is excluded from sharding. func (a *QueryAnalyzer) Analyze(query string) (QueryAnalysis, error) { - expr, err := parser.ParseExpr(query) + expr, err := extpromql.ParserExpr(query) if err != nil { return nonShardableQuery(), err } From 618b53883bdb9ba94d5bceb47ee295a942dd11f8 Mon Sep 17 00:00:00 2001 From: Pedro Tanaka Date: Mon, 6 May 2024 13:45:55 +0200 Subject: [PATCH 2/8] Adding new failint for the Parse() method Signed-off-by: Pedro Tanaka --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index bee70ee848..29e1d5cc87 100644 --- a/Makefile +++ b/Makefile @@ -404,6 +404,7 @@ NewHistorgram,NewHistogramVec,NewSummary,NewSummaryVec}=github.com/prometheus/cl NewCounterVec,NewCounterVec,NewGauge,NewGaugeVec,NewGaugeFunc,NewHistorgram,NewHistogramVec,NewSummary,NewSummaryVec},\ github.com/NYTimes/gziphandler.{GzipHandler}=github.com/klauspost/compress/gzhttp.{GzipHandler},\ sync/atomic=go.uber.org/atomic,github.com/cortexproject/cortex=github.com/thanos-io/thanos/internal/cortex,\ +github.com/prometheus/prometheus/promql/parser.{ParseExpr,ParseMetricSelector}=github.com/thanos-io/thanos/pkg/extpromql.{ParseExpr,ParseMetricSelector},\ io/ioutil.{Discard,NopCloser,ReadAll,ReadDir,ReadFile,TempDir,TempFile,Writefile}" $(shell go list ./... | grep -v "internal/cortex") @$(FAILLINT) -paths "fmt.{Print,Println,Sprint}" -ignore-tests ./... @echo ">> linting all of the Go files GOGC=${GOGC}" From dc06aaabbb086f96c27ea75c982c6ab26a16481d Mon Sep 17 00:00:00 2001 From: Pedro Tanaka Date: Mon, 6 May 2024 13:46:16 +0200 Subject: [PATCH 3/8] Adding new method for ParseMetricSelector Signed-off-by: Pedro Tanaka --- pkg/extpromql/parser.go | 31 +++++++++++++++++++ pkg/extpromql/parser_test.go | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 pkg/extpromql/parser_test.go diff --git a/pkg/extpromql/parser.go b/pkg/extpromql/parser.go index 72302b7ef2..5fb4784191 100644 --- a/pkg/extpromql/parser.go +++ b/pkg/extpromql/parser.go @@ -4,12 +4,43 @@ package extpromql import ( + "fmt" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/promql-engine/execution/function" ) +// ParserExpr parses the input PromQL expression and returns the parsed representation. func ParserExpr(input string) (parser.Expr, error) { p := parser.NewParser(input, parser.WithFunctions(function.XFunctions)) defer p.Close() return p.ParseExpr() } + +// ParseMetricSelector parses the provided textual metric selector into a list of +// label matchers. +func ParseMetricSelector(input string) ([]*labels.Matcher, error) { + // Parse the input string as a PromQL expression. + expr, err := parser.ParseExpr(input) + if err != nil { + return nil, err + } + + // The type of the expression should be *parser.VectorSelector. + vs, ok := expr.(*parser.VectorSelector) + if !ok { + return nil, fmt.Errorf("expected type *parser.VectorSelector, got %T", expr) + } + + // Convert the label matchers from the vector selector to the desired type. + matchers := make([]*labels.Matcher, len(vs.LabelMatchers)) + for i, lm := range vs.LabelMatchers { + matchers[i] = &labels.Matcher{ + Type: labels.MatchType(lm.Type), + Name: lm.Name, + Value: lm.Value, + } + } + + return matchers, nil +} diff --git a/pkg/extpromql/parser_test.go b/pkg/extpromql/parser_test.go new file mode 100644 index 0000000000..3c2688e9be --- /dev/null +++ b/pkg/extpromql/parser_test.go @@ -0,0 +1,60 @@ +package extpromql_test + +import ( + "fmt" + "github.com/efficientgo/core/testutil" + "github.com/prometheus/prometheus/model/labels" + "github.com/thanos-io/thanos/pkg/extpromql" + "testing" + + "github.com/prometheus/prometheus/promql/parser" +) + +func TestParseMetricSelector(t *testing.T) { + testCases := []struct { + name string + input string + }{ + { + name: "single selector", + input: `http_requests_total{method="GET"}`, + }, + { + name: "empty selectors", + input: `process_cpu_seconds_total`, + }, + { + name: "multiple selectors", + input: `http_requests_total{method="GET",code="200"}`, + }, + { + name: "multiple selectors with different matchers", + input: `http_requests_total{method="GET",code!="200"}`, + }, + { + name: "multiple selectors with regex", + input: `http_requests_total{method="GET",code=~"2.*"}`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + // Call your implementation of ParseMetricSelector + got, err := extpromql.ParseMetricSelector(tc.input) + if err != nil { + t.Fatalf("ParseMetricSelector failed: %v", err) + } + + want, err := parser.ParseMetricSelector(tc.input) + if err != nil { + t.Fatalf("ParseMetricSelector failed: %v", err) + } + + testutil.Equals(t, stringFmt(want), stringFmt(got)) + }) + } +} + +func stringFmt(got []*labels.Matcher) string { + return fmt.Sprintf("%v", got) +} From 39d4e021221d083fd0eafd5509a1c1b9641abb07 Mon Sep 17 00:00:00 2001 From: Pedro Tanaka Date: Mon, 6 May 2024 13:46:39 +0200 Subject: [PATCH 4/8] Fixing missing imports Extending test to check behavior More missing imports Signed-off-by: Pedro Tanaka --- .../querier/queryrange/split_by_interval.go | 1 + pkg/api/query/v1_test.go | 26 ++++++++++++++++++- pkg/exemplars/proxy.go | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/internal/cortex/querier/queryrange/split_by_interval.go b/internal/cortex/querier/queryrange/split_by_interval.go index cb142d07ed..b55ceb7c1b 100644 --- a/internal/cortex/querier/queryrange/split_by_interval.go +++ b/internal/cortex/querier/queryrange/split_by_interval.go @@ -5,6 +5,7 @@ package queryrange import ( "context" + "github.com/thanos-io/thanos/pkg/extpromql" "net/http" "time" diff --git a/pkg/api/query/v1_test.go b/pkg/api/query/v1_test.go index a820abe351..8e590de7d1 100644 --- a/pkg/api/query/v1_test.go +++ b/pkg/api/query/v1_test.go @@ -712,7 +712,7 @@ func TestQueryAnalyzeEndpoints(t *testing.T) { Reg: nil, MaxSamples: 10000, Timeout: timeout, - }, nil, false) + }, nil, true) api := &QueryAPI{ baseAPI: &baseAPI.BaseAPI{ Now: func() time.Time { return now }, @@ -749,6 +749,30 @@ func TestQueryAnalyzeEndpoints(t *testing.T) { QueryAnalysis: queryTelemetry{}, }, }, + { + endpoint: api.queryRange, + query: url.Values{ + "query": []string{"xrate(up[2m])"}, + "start": []string{"0"}, + "end": []string{"500"}, + }, + response: &queryData{ + ResultType: parser.ValueTypeMatrix, + Result: promql.Matrix{ + promql.Series{ + Floats: func(end, step float64) []promql.FPoint { + var res []promql.FPoint + for v := float64(0); v <= end; v += step { + res = append(res, promql.FPoint{F: v, T: timestamp.FromTime(start.Add(time.Duration(v) * time.Second))}) + } + return res + }(500, 1), + Metric: nil, + }, + }, + QueryAnalysis: queryTelemetry{}, + }, + }, { endpoint: api.queryRange, query: url.Values{ diff --git a/pkg/exemplars/proxy.go b/pkg/exemplars/proxy.go index 510914fadb..a4b8bf6cc7 100644 --- a/pkg/exemplars/proxy.go +++ b/pkg/exemplars/proxy.go @@ -5,6 +5,7 @@ package exemplars import ( "context" + "github.com/thanos-io/thanos/pkg/extpromql" "io" "strings" From f8ace62409df23206cda039a3fd7ec01be23c6b0 Mon Sep 17 00:00:00 2001 From: Pedro Tanaka Date: Mon, 6 May 2024 13:48:52 +0200 Subject: [PATCH 5/8] Fixing method name Signed-off-by: Pedro Tanaka --- cmd/thanos/rule.go | 2 +- internal/cortex/querier/queryrange/results_cache.go | 4 ++-- internal/cortex/querier/queryrange/split_by_interval.go | 2 +- .../cortex/querier/queryrange/split_by_interval_test.go | 2 +- pkg/exemplars/multitsdb.go | 2 +- pkg/exemplars/proxy.go | 2 +- pkg/exemplars/proxy_test.go | 2 +- pkg/extpromql/parser.go | 6 +++--- pkg/queryfrontend/queryinstant_codec.go | 2 +- pkg/querysharding/analyzer.go | 2 +- pkg/tenancy/tenancy.go | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cmd/thanos/rule.go b/cmd/thanos/rule.go index 3523c5d5c5..21c1e9fb11 100644 --- a/cmd/thanos/rule.go +++ b/cmd/thanos/rule.go @@ -952,7 +952,7 @@ func queryFuncCreator( queryAPIClients := grpcEndpointSet.GetQueryAPIClients() for _, i := range rand.Perm(len(queryAPIClients)) { e := query.NewRemoteEngine(logger, queryAPIClients[i], query.Opts{}) - expr, err := extpromql.ParserExpr(qs) + expr, err := extpromql.ParseExpr(qs) if err != nil { level.Error(logger).Log("err", err, "query", qs) continue diff --git a/internal/cortex/querier/queryrange/results_cache.go b/internal/cortex/querier/queryrange/results_cache.go index 2e0a16d9b0..a3399e2551 100644 --- a/internal/cortex/querier/queryrange/results_cache.go +++ b/internal/cortex/querier/queryrange/results_cache.go @@ -326,7 +326,7 @@ func (s resultsCache) isAtModifierCachable(r Request, maxCacheTime int64) bool { if !strings.Contains(query, "@") { return true } - expr, err := extpromql.ParserExpr(query) + expr, err := extpromql.ParseExpr(query) if err != nil { // We are being pessimistic in such cases. level.Warn(s.logger).Log("msg", "failed to parse query, considering @ modifier as not cachable", "query", query, "err", err) @@ -371,7 +371,7 @@ func (s resultsCache) isOffsetCachable(r Request) bool { if !strings.Contains(query, "offset") { return true } - expr, err := extpromql.ParserExpr(query) + expr, err := extpromql.ParseExpr(query) if err != nil { level.Warn(s.logger).Log("msg", "failed to parse query, considering offset as not cachable", "query", query, "err", err) return false diff --git a/internal/cortex/querier/queryrange/split_by_interval.go b/internal/cortex/querier/queryrange/split_by_interval.go index b55ceb7c1b..2ae53f7c50 100644 --- a/internal/cortex/querier/queryrange/split_by_interval.go +++ b/internal/cortex/querier/queryrange/split_by_interval.go @@ -98,7 +98,7 @@ func splitQuery(r Request, interval time.Duration) ([]Request, error) { // For example given the start of the query is 10.00, `http_requests_total[1h] @ start()` query will be replaced with `http_requests_total[1h] @ 10.00` // If the modifier is already a constant, it will be returned as is. func EvaluateAtModifierFunction(query string, start, end int64) (string, error) { - expr, err := extpromql.ParserExpr(query) + expr, err := extpromql.ParseExpr(query) if err != nil { return "", httpgrpc.Errorf(http.StatusBadRequest, `{"status": "error", "error": "%s"}`, err) } diff --git a/internal/cortex/querier/queryrange/split_by_interval_test.go b/internal/cortex/querier/queryrange/split_by_interval_test.go index 369f4d71b5..68b67d95dc 100644 --- a/internal/cortex/querier/queryrange/split_by_interval_test.go +++ b/internal/cortex/querier/queryrange/split_by_interval_test.go @@ -395,7 +395,7 @@ func Test_evaluateAtModifier(t *testing.T) { require.Equal(t, tt.expectedErrorCode, int(httpResp.Code)) } else { require.NoError(t, err) - expectedExpr, err := extpromql.ParserExpr(tt.expected) + expectedExpr, err := extpromql.ParseExpr(tt.expected) require.NoError(t, err) require.Equal(t, expectedExpr.String(), out) } diff --git a/pkg/exemplars/multitsdb.go b/pkg/exemplars/multitsdb.go index ff1442ae8f..985f0b6bfe 100644 --- a/pkg/exemplars/multitsdb.go +++ b/pkg/exemplars/multitsdb.go @@ -27,7 +27,7 @@ func NewMultiTSDB(tsdbExemplarsServers func() map[string]*TSDB) *MultiTSDB { // Exemplars returns all specified exemplars from a MultiTSDB instance. func (m *MultiTSDB) Exemplars(r *exemplarspb.ExemplarsRequest, s exemplarspb.Exemplars_ExemplarsServer) error { - expr, err := extpromql.ParserExpr(r.Query) + expr, err := extpromql.ParseExpr(r.Query) if err != nil { return status.Error(codes.Internal, err.Error()) } diff --git a/pkg/exemplars/proxy.go b/pkg/exemplars/proxy.go index a4b8bf6cc7..bb6858cfa5 100644 --- a/pkg/exemplars/proxy.go +++ b/pkg/exemplars/proxy.go @@ -60,7 +60,7 @@ func (s *Proxy) Exemplars(req *exemplarspb.ExemplarsRequest, srv exemplarspb.Exe span, ctx := tracing.StartSpan(srv.Context(), "proxy_exemplars") defer span.Finish() - expr, err := extpromql.ParserExpr(req.Query) + expr, err := extpromql.ParseExpr(req.Query) if err != nil { return err } diff --git a/pkg/exemplars/proxy_test.go b/pkg/exemplars/proxy_test.go index f1cb19001c..0ee679d874 100644 --- a/pkg/exemplars/proxy_test.go +++ b/pkg/exemplars/proxy_test.go @@ -55,7 +55,7 @@ func (t *testExemplarClient) Recv() (*exemplarspb.ExemplarsResponse, error) { } func (t *testExemplarClient) Exemplars(ctx context.Context, in *exemplarspb.ExemplarsRequest, opts ...grpc.CallOption) (exemplarspb.Exemplars_ExemplarsClient, error) { - expr, err := extpromql.ParserExpr(in.Query) + expr, err := extpromql.ParseExpr(in.Query) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } diff --git a/pkg/extpromql/parser.go b/pkg/extpromql/parser.go index 5fb4784191..a66c7836f6 100644 --- a/pkg/extpromql/parser.go +++ b/pkg/extpromql/parser.go @@ -10,8 +10,8 @@ import ( "github.com/thanos-io/promql-engine/execution/function" ) -// ParserExpr parses the input PromQL expression and returns the parsed representation. -func ParserExpr(input string) (parser.Expr, error) { +// ParseExpr parses the input PromQL expression and returns the parsed representation. +func ParseExpr(input string) (parser.Expr, error) { p := parser.NewParser(input, parser.WithFunctions(function.XFunctions)) defer p.Close() return p.ParseExpr() @@ -21,7 +21,7 @@ func ParserExpr(input string) (parser.Expr, error) { // label matchers. func ParseMetricSelector(input string) ([]*labels.Matcher, error) { // Parse the input string as a PromQL expression. - expr, err := parser.ParseExpr(input) + expr, err := ParseExpr(input) if err != nil { return nil, err } diff --git a/pkg/queryfrontend/queryinstant_codec.go b/pkg/queryfrontend/queryinstant_codec.go index ff3c0b1885..57fc63ea8f 100644 --- a/pkg/queryfrontend/queryinstant_codec.go +++ b/pkg/queryfrontend/queryinstant_codec.go @@ -371,7 +371,7 @@ const ( ) func sortPlanForQuery(q string) (sortPlan, error) { - expr, err := extpromql.ParserExpr(q) + expr, err := extpromql.ParseExpr(q) if err != nil { return 0, err } diff --git a/pkg/querysharding/analyzer.go b/pkg/querysharding/analyzer.go index c6f7e372e5..a2108673c0 100644 --- a/pkg/querysharding/analyzer.go +++ b/pkg/querysharding/analyzer.go @@ -89,7 +89,7 @@ func (a *CachedQueryAnalyzer) Analyze(query string) (QueryAnalysis, error) { // // The le label is excluded from sharding. func (a *QueryAnalyzer) Analyze(query string) (QueryAnalysis, error) { - expr, err := extpromql.ParserExpr(query) + expr, err := extpromql.ParseExpr(query) if err != nil { return nonShardableQuery(), err } diff --git a/pkg/tenancy/tenancy.go b/pkg/tenancy/tenancy.go index a350062194..1a1c979641 100644 --- a/pkg/tenancy/tenancy.go +++ b/pkg/tenancy/tenancy.go @@ -150,7 +150,7 @@ func EnforceQueryTenancy(tenantLabel string, tenant string, query string) (strin e := injectproxy.NewEnforcer(false, labelMatcher) - expr, err := extpromql.ParserExpr(query) + expr, err := extpromql.ParseExpr(query) if err != nil { return "", errors.Wrap(err, "error parsing query string, when enforcing tenenacy") } From 422f8f4de9d1cf1531d4991427ec4ff231ac0858 Mon Sep 17 00:00:00 2001 From: Pedro Tanaka Date: Mon, 6 May 2024 13:57:58 +0200 Subject: [PATCH 6/8] Solving references to forbidden functions Signed-off-by: Pedro Tanaka --- pkg/api/query/grpc_test.go | 4 ++-- pkg/api/query/v1.go | 3 ++- pkg/block/metadata/meta.go | 4 ++-- pkg/exemplars/proxy.go | 2 +- pkg/exemplars/proxy_test.go | 5 ++--- pkg/extpromql/parser.go | 2 ++ pkg/extpromql/parser_test.go | 18 +++++++++++------- pkg/query/remote_engine_test.go | 4 ++-- pkg/query/test_test.go | 6 ++++-- pkg/queryfrontend/queryinstant_codec.go | 4 ++-- pkg/queryfrontend/queryrange_codec.go | 5 ++--- pkg/querysharding/analyzer.go | 3 ++- pkg/rules/rules.go | 4 ++-- pkg/store/storepb/custom_test.go | 4 ++-- pkg/tenancy/tenancy.go | 3 +-- 15 files changed, 39 insertions(+), 32 deletions(-) diff --git a/pkg/api/query/grpc_test.go b/pkg/api/query/grpc_test.go index 4885126c46..868ed3d411 100644 --- a/pkg/api/query/grpc_test.go +++ b/pkg/api/query/grpc_test.go @@ -13,7 +13,6 @@ import ( "github.com/go-kit/log" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/prometheus/promql" - "github.com/prometheus/prometheus/promql/parser" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/util/annotations" "github.com/thanos-io/promql-engine/logicalplan" @@ -21,6 +20,7 @@ import ( "github.com/thanos-io/thanos/pkg/api/query/querypb" "github.com/thanos-io/thanos/pkg/component" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/query" "github.com/thanos-io/thanos/pkg/store" ) @@ -36,7 +36,7 @@ func TestGRPCQueryAPIWithQueryPlan(t *testing.T) { } api := NewGRPCAPI(time.Now, nil, queryableCreator, engineFactory, querypb.EngineType_thanos, lookbackDeltaFunc, 0) - expr, err := parser.ParseExpr("metric") + expr, err := extpromql.ParseExpr("metric") testutil.Ok(t, err) lplan := logicalplan.NewFromAST(expr, &equery.Options{}, logicalplan.PlanOptions{}) testutil.Ok(t, err) diff --git a/pkg/api/query/v1.go b/pkg/api/query/v1.go index 0b8bb93fb2..9454dfd65f 100644 --- a/pkg/api/query/v1.go +++ b/pkg/api/query/v1.go @@ -52,6 +52,7 @@ import ( "github.com/thanos-io/thanos/pkg/exemplars" "github.com/thanos-io/thanos/pkg/exemplars/exemplarspb" extpromhttp "github.com/thanos-io/thanos/pkg/extprom/http" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/gate" "github.com/thanos-io/thanos/pkg/logging" "github.com/thanos-io/thanos/pkg/metadata" @@ -374,7 +375,7 @@ func (qapi *QueryAPI) parseStoreDebugMatchersParam(r *http.Request) (storeMatche } for _, s := range r.Form[StoreMatcherParam] { - matchers, err := parser.ParseMetricSelector(s) + matchers, err := extpromql.ParseMetricSelector(s) if err != nil { return nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } diff --git a/pkg/block/metadata/meta.go b/pkg/block/metadata/meta.go index a479ee242d..11567fb06e 100644 --- a/pkg/block/metadata/meta.go +++ b/pkg/block/metadata/meta.go @@ -20,12 +20,12 @@ import ( "github.com/pkg/errors" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/model/relabel" - "github.com/prometheus/prometheus/promql/parser" "github.com/prometheus/prometheus/tsdb" "github.com/prometheus/prometheus/tsdb/fileutil" "github.com/prometheus/prometheus/tsdb/tombstones" "gopkg.in/yaml.v3" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/runutil" ) @@ -136,7 +136,7 @@ type Rewrite struct { type Matchers []*labels.Matcher func (m *Matchers) UnmarshalYAML(value *yaml.Node) (err error) { - *m, err = parser.ParseMetricSelector(value.Value) + *m, err = extpromql.ParseMetricSelector(value.Value) if err != nil { return errors.Wrapf(err, "parse metric selector %v", value.Value) } diff --git a/pkg/exemplars/proxy.go b/pkg/exemplars/proxy.go index bb6858cfa5..c7d8fb911c 100644 --- a/pkg/exemplars/proxy.go +++ b/pkg/exemplars/proxy.go @@ -5,7 +5,6 @@ package exemplars import ( "context" - "github.com/thanos-io/thanos/pkg/extpromql" "io" "strings" @@ -20,6 +19,7 @@ import ( "google.golang.org/grpc/status" "github.com/thanos-io/thanos/pkg/exemplars/exemplarspb" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/store" "github.com/thanos-io/thanos/pkg/store/storepb" "github.com/thanos-io/thanos/pkg/tracing" diff --git a/pkg/exemplars/proxy_test.go b/pkg/exemplars/proxy_test.go index 0ee679d874..0e0ca58f8e 100644 --- a/pkg/exemplars/proxy_test.go +++ b/pkg/exemplars/proxy_test.go @@ -6,13 +6,13 @@ package exemplars import ( "context" "fmt" - "github.com/thanos-io/thanos/pkg/extpromql" "io" "os" "reflect" "sync" "testing" + "github.com/efficientgo/core/testutil" "github.com/go-kit/log" "github.com/pkg/errors" "github.com/prometheus/prometheus/model/labels" @@ -22,9 +22,8 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "github.com/efficientgo/core/testutil" - "github.com/thanos-io/thanos/pkg/exemplars/exemplarspb" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/store/labelpb" "github.com/thanos-io/thanos/pkg/store/storepb" ) diff --git a/pkg/extpromql/parser.go b/pkg/extpromql/parser.go index a66c7836f6..5284e5e9f3 100644 --- a/pkg/extpromql/parser.go +++ b/pkg/extpromql/parser.go @@ -5,8 +5,10 @@ package extpromql import ( "fmt" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" + "github.com/thanos-io/promql-engine/execution/function" ) diff --git a/pkg/extpromql/parser_test.go b/pkg/extpromql/parser_test.go index 3c2688e9be..1c0cfebdcd 100644 --- a/pkg/extpromql/parser_test.go +++ b/pkg/extpromql/parser_test.go @@ -1,13 +1,17 @@ +// Copyright (c) The Thanos Authors. +// Licensed under the Apache License 2.0. + package extpromql_test import ( "fmt" - "github.com/efficientgo/core/testutil" - "github.com/prometheus/prometheus/model/labels" - "github.com/thanos-io/thanos/pkg/extpromql" "testing" + "github.com/efficientgo/core/testutil" + "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" + + "github.com/thanos-io/thanos/pkg/extpromql" ) func TestParseMetricSelector(t *testing.T) { @@ -39,13 +43,13 @@ func TestParseMetricSelector(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - // Call your implementation of ParseMetricSelector - got, err := extpromql.ParseMetricSelector(tc.input) + //lint:ignore faillint Testing against prometheus parser. + want, err := parser.ParseMetricSelector(tc.input) if err != nil { - t.Fatalf("ParseMetricSelector failed: %v", err) + t.Fatalf("Prometheus ParseMetricSelector failed: %v", err) } - want, err := parser.ParseMetricSelector(tc.input) + got, err := extpromql.ParseMetricSelector(tc.input) if err != nil { t.Fatalf("ParseMetricSelector failed: %v", err) } diff --git a/pkg/query/remote_engine_test.go b/pkg/query/remote_engine_test.go index bca79598e5..137bd73da1 100644 --- a/pkg/query/remote_engine_test.go +++ b/pkg/query/remote_engine_test.go @@ -14,12 +14,12 @@ import ( "github.com/go-kit/log" "github.com/pkg/errors" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/promql-engine/logicalplan" "github.com/thanos-io/promql-engine/query" "google.golang.org/grpc" "github.com/thanos-io/thanos/pkg/api/query/querypb" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/info/infopb" "github.com/thanos-io/thanos/pkg/store/labelpb" ) @@ -34,7 +34,7 @@ func TestRemoteEngine_Warnings(t *testing.T) { end = time.Unix(120, 0) step = 30 * time.Second ) - qryExpr, err := parser.ParseExpr("up") + qryExpr, err := extpromql.ParseExpr("up") testutil.Ok(t, err) plan := logicalplan.NewFromAST(qryExpr, &query.Options{ diff --git a/pkg/query/test_test.go b/pkg/query/test_test.go index b8457870ca..d8af78c66d 100644 --- a/pkg/query/test_test.go +++ b/pkg/query/test_test.go @@ -24,6 +24,8 @@ import ( "github.com/prometheus/prometheus/promql/parser/posrange" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/util/teststorage" + + "github.com/thanos-io/thanos/pkg/extpromql" ) var ( @@ -261,7 +263,7 @@ func ParseStore(lines []string, i int) (int, *storeCmd, error) { } parts := patStore.FindStringSubmatch(lines[i]) - m, err := parser.ParseMetricSelector(parts[1]) + m, err := extpromql.ParseMetricSelector(parts[1]) if err != nil { return i, nil, raise(i, "invalid matcher definition %q: %s", parts[1], err) } @@ -322,7 +324,7 @@ func ParseEval(lines []string, i int) (int, *evalCmd, error) { at = parts[2] expr = parts[3] ) - _, err := parser.ParseExpr(expr) + _, err := extpromql.ParseExpr(expr) if err != nil { if perr, ok := err.(*parser.ParseErr); ok { perr.LineOffset = i diff --git a/pkg/queryfrontend/queryinstant_codec.go b/pkg/queryfrontend/queryinstant_codec.go index 57fc63ea8f..a44bb7d94e 100644 --- a/pkg/queryfrontend/queryinstant_codec.go +++ b/pkg/queryfrontend/queryinstant_codec.go @@ -7,7 +7,6 @@ import ( "bytes" "context" "encoding/json" - "github.com/thanos-io/thanos/pkg/extpromql" "io" "net/http" "net/url" @@ -18,14 +17,15 @@ import ( "github.com/opentracing/opentracing-go" otlog "github.com/opentracing/opentracing-go/log" "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/promql/parser" "github.com/weaveworks/common/httpgrpc" - "github.com/prometheus/prometheus/promql/parser" "github.com/thanos-io/thanos/internal/cortex/cortexpb" "github.com/thanos-io/thanos/internal/cortex/querier/queryrange" cortexutil "github.com/thanos-io/thanos/internal/cortex/util" "github.com/thanos-io/thanos/internal/cortex/util/spanlogger" queryv1 "github.com/thanos-io/thanos/pkg/api/query" + "github.com/thanos-io/thanos/pkg/extpromql" ) // queryInstantCodec is used to encode/decode Thanos instant query requests and responses. diff --git a/pkg/queryfrontend/queryrange_codec.go b/pkg/queryfrontend/queryrange_codec.go index fca6ea7fc7..4de0dd387e 100644 --- a/pkg/queryfrontend/queryrange_codec.go +++ b/pkg/queryfrontend/queryrange_codec.go @@ -16,13 +16,12 @@ import ( "github.com/prometheus/common/model" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql/parser" "github.com/weaveworks/common/httpgrpc" "github.com/thanos-io/thanos/internal/cortex/querier/queryrange" cortexutil "github.com/thanos-io/thanos/internal/cortex/util" - queryv1 "github.com/thanos-io/thanos/pkg/api/query" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/store/storepb" ) @@ -269,7 +268,7 @@ func parsePartialResponseParam(s string, defaultEnablePartialResponse bool) (boo func parseMatchersParam(ss url.Values, matcherParam string) ([][]*labels.Matcher, error) { matchers := make([][]*labels.Matcher, 0, len(ss[matcherParam])) for _, s := range ss[matcherParam] { - ms, err := parser.ParseMetricSelector(s) + ms, err := extpromql.ParseMetricSelector(s) if err != nil { return nil, httpgrpc.Errorf(http.StatusBadRequest, errCannotParse, matcherParam) } diff --git a/pkg/querysharding/analyzer.go b/pkg/querysharding/analyzer.go index a2108673c0..80cb8cb3a8 100644 --- a/pkg/querysharding/analyzer.go +++ b/pkg/querysharding/analyzer.go @@ -18,11 +18,12 @@ package querysharding import ( "fmt" - "github.com/thanos-io/thanos/pkg/extpromql" lru "github.com/hashicorp/golang-lru/v2" "github.com/prometheus/common/model" "github.com/prometheus/prometheus/promql/parser" + + "github.com/thanos-io/thanos/pkg/extpromql" ) var ( diff --git a/pkg/rules/rules.go b/pkg/rules/rules.go index 72f5db3a44..1777a45181 100644 --- a/pkg/rules/rules.go +++ b/pkg/rules/rules.go @@ -12,9 +12,9 @@ import ( "github.com/pkg/errors" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql/parser" "github.com/prometheus/prometheus/util/annotations" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/rules/rulespb" "github.com/thanos-io/thanos/pkg/tracing" ) @@ -64,7 +64,7 @@ func (rr *GRPCClient) Rules(ctx context.Context, req *rulespb.RulesRequest) (*ru var err error matcherSets := make([][]*labels.Matcher, len(req.MatcherString)) for i, s := range req.MatcherString { - matcherSets[i], err = parser.ParseMetricSelector(s) + matcherSets[i], err = extpromql.ParseMetricSelector(s) if err != nil { return nil, nil, errors.Wrap(err, "parser ParseMetricSelector") } diff --git a/pkg/store/storepb/custom_test.go b/pkg/store/storepb/custom_test.go index eff2be6e80..91e2a1a669 100644 --- a/pkg/store/storepb/custom_test.go +++ b/pkg/store/storepb/custom_test.go @@ -12,10 +12,10 @@ import ( "github.com/pkg/errors" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql/parser" "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/efficientgo/core/testutil" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/store/labelpb" ) @@ -520,7 +520,7 @@ func TestMatchersToString_Translate(t *testing.T) { testutil.Equals(t, c.expected, MatchersToString(ms...)) // Is this parsable? - promMsParsed, err := parser.ParseMetricSelector(c.expected) + promMsParsed, err := extpromql.ParseMetricSelector(c.expected) testutil.Ok(t, err) testutil.Assert(t, len(promMs) == len(promMsParsed)) for i := 0; i < len(promMs); i++ { diff --git a/pkg/tenancy/tenancy.go b/pkg/tenancy/tenancy.go index 1a1c979641..9da1372933 100644 --- a/pkg/tenancy/tenancy.go +++ b/pkg/tenancy/tenancy.go @@ -11,7 +11,6 @@ import ( "github.com/pkg/errors" "github.com/prometheus-community/prom-label-proxy/injectproxy" "github.com/prometheus/prometheus/model/labels" - "github.com/prometheus/prometheus/promql/parser" "google.golang.org/grpc/metadata" "github.com/thanos-io/thanos/pkg/extpromql" @@ -180,7 +179,7 @@ func getLabelMatchers(formMatchers []string, tenant string, enforceTenancy bool, } for _, s := range formMatchers { - matchers, err := parser.ParseMetricSelector(s) + matchers, err := extpromql.ParseMetricSelector(s) if err != nil { return nil, err } From 5ced8c3c382bd947db2325689938321a2f63bf16 Mon Sep 17 00:00:00 2001 From: Pedro Tanaka Date: Mon, 6 May 2024 16:12:57 +0200 Subject: [PATCH 7/8] Treating promql validation from ParseExpr Signed-off-by: Pedro Tanaka --- pkg/extpromql/parser.go | 21 ++++++++++++++++----- pkg/extpromql/parser_test.go | 4 ++++ pkg/store/storepb/custom_test.go | 3 ++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/pkg/extpromql/parser.go b/pkg/extpromql/parser.go index 5284e5e9f3..1a43fd8a05 100644 --- a/pkg/extpromql/parser.go +++ b/pkg/extpromql/parser.go @@ -5,7 +5,9 @@ package extpromql import ( "fmt" + "strings" + "github.com/pkg/errors" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql/parser" @@ -22,23 +24,22 @@ func ParseExpr(input string) (parser.Expr, error) { // ParseMetricSelector parses the provided textual metric selector into a list of // label matchers. func ParseMetricSelector(input string) ([]*labels.Matcher, error) { - // Parse the input string as a PromQL expression. expr, err := ParseExpr(input) - if err != nil { + // because of the AST checking present in the ParseExpr function, + // we need to ignore the error if it is just the check for empty name matcher. + if err != nil && !isEmptyNameMatcherErr(err) { return nil, err } - // The type of the expression should be *parser.VectorSelector. vs, ok := expr.(*parser.VectorSelector) if !ok { return nil, fmt.Errorf("expected type *parser.VectorSelector, got %T", expr) } - // Convert the label matchers from the vector selector to the desired type. matchers := make([]*labels.Matcher, len(vs.LabelMatchers)) for i, lm := range vs.LabelMatchers { matchers[i] = &labels.Matcher{ - Type: labels.MatchType(lm.Type), + Type: lm.Type, Name: lm.Name, Value: lm.Value, } @@ -46,3 +47,13 @@ func ParseMetricSelector(input string) ([]*labels.Matcher, error) { return matchers, nil } + +func isEmptyNameMatcherErr(err error) bool { + var parseErrs parser.ParseErrors + if errors.As(err, &parseErrs) { + return len(parseErrs) == 1 && + strings.HasSuffix(parseErrs[0].Error(), "vector selector must contain at least one non-empty matcher") + } + + return false +} diff --git a/pkg/extpromql/parser_test.go b/pkg/extpromql/parser_test.go index 1c0cfebdcd..72ef7b26cb 100644 --- a/pkg/extpromql/parser_test.go +++ b/pkg/extpromql/parser_test.go @@ -39,6 +39,10 @@ func TestParseMetricSelector(t *testing.T) { name: "multiple selectors with regex", input: `http_requests_total{method="GET",code=~"2.*"}`, }, + { + name: "selector with negative regex", + input: `{code!~"2.*"}`, + }, } for _, tc := range testCases { diff --git a/pkg/store/storepb/custom_test.go b/pkg/store/storepb/custom_test.go index 91e2a1a669..4a1c6d83c2 100644 --- a/pkg/store/storepb/custom_test.go +++ b/pkg/store/storepb/custom_test.go @@ -15,6 +15,7 @@ import ( "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/efficientgo/core/testutil" + "github.com/thanos-io/thanos/pkg/extpromql" "github.com/thanos-io/thanos/pkg/store/labelpb" ) @@ -521,7 +522,7 @@ func TestMatchersToString_Translate(t *testing.T) { // Is this parsable? promMsParsed, err := extpromql.ParseMetricSelector(c.expected) - testutil.Ok(t, err) + testutil.Ok(t, err, "unexpected error parsing %q", c.expected) testutil.Assert(t, len(promMs) == len(promMsParsed)) for i := 0; i < len(promMs); i++ { testutil.Equals(t, promMs[i].String(), promMsParsed[i].String()) From b2da2439af38a6045a4269dcfdfcae5866326039 Mon Sep 17 00:00:00 2001 From: Pedro Tanaka Date: Mon, 6 May 2024 16:55:55 +0200 Subject: [PATCH 8/8] fixing funcs Signed-off-by: Pedro Tanaka --- pkg/api/query/v1_test.go | 26 +------------------------- pkg/extpromql/parser.go | 9 ++++++++- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/pkg/api/query/v1_test.go b/pkg/api/query/v1_test.go index 8e590de7d1..a820abe351 100644 --- a/pkg/api/query/v1_test.go +++ b/pkg/api/query/v1_test.go @@ -712,7 +712,7 @@ func TestQueryAnalyzeEndpoints(t *testing.T) { Reg: nil, MaxSamples: 10000, Timeout: timeout, - }, nil, true) + }, nil, false) api := &QueryAPI{ baseAPI: &baseAPI.BaseAPI{ Now: func() time.Time { return now }, @@ -749,30 +749,6 @@ func TestQueryAnalyzeEndpoints(t *testing.T) { QueryAnalysis: queryTelemetry{}, }, }, - { - endpoint: api.queryRange, - query: url.Values{ - "query": []string{"xrate(up[2m])"}, - "start": []string{"0"}, - "end": []string{"500"}, - }, - response: &queryData{ - ResultType: parser.ValueTypeMatrix, - Result: promql.Matrix{ - promql.Series{ - Floats: func(end, step float64) []promql.FPoint { - var res []promql.FPoint - for v := float64(0); v <= end; v += step { - res = append(res, promql.FPoint{F: v, T: timestamp.FromTime(start.Add(time.Duration(v) * time.Second))}) - } - return res - }(500, 1), - Metric: nil, - }, - }, - QueryAnalysis: queryTelemetry{}, - }, - }, { endpoint: api.queryRange, query: url.Values{ diff --git a/pkg/extpromql/parser.go b/pkg/extpromql/parser.go index 1a43fd8a05..43d7188fdc 100644 --- a/pkg/extpromql/parser.go +++ b/pkg/extpromql/parser.go @@ -16,7 +16,14 @@ import ( // ParseExpr parses the input PromQL expression and returns the parsed representation. func ParseExpr(input string) (parser.Expr, error) { - p := parser.NewParser(input, parser.WithFunctions(function.XFunctions)) + allFuncs := make(map[string]*parser.Function, len(function.XFunctions)+len(parser.Functions)) + for k, v := range parser.Functions { + allFuncs[k] = v + } + for k, v := range function.XFunctions { + allFuncs[k] = v + } + p := parser.NewParser(input, parser.WithFunctions(allFuncs)) defer p.Close() return p.ParseExpr() }