Skip to content

Commit

Permalink
Treating promql validation from ParseExpr
Browse files Browse the repository at this point in the history
Signed-off-by: Pedro Tanaka <pedro.tanaka@shopify.com>
  • Loading branch information
pedro-stanaka committed May 6, 2024
1 parent 422f8f4 commit 5ced8c3
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 6 deletions.
21 changes: 16 additions & 5 deletions pkg/extpromql/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -22,27 +24,36 @@ 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,
}
}

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
}
4 changes: 4 additions & 0 deletions pkg/extpromql/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion pkg/store/storepb/custom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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())
Expand Down

0 comments on commit 5ced8c3

Please sign in to comment.