From 0a43e788affaa307378c180b65f973a662f4c171 Mon Sep 17 00:00:00 2001 From: Oleg Zaytsev Date: Wed, 1 Sep 2021 09:48:18 +0200 Subject: [PATCH] Allow VectorSelector.String() without matchers (#9282) * Allow VectorSelector.String() without matchers Previously this method was panicking because it was trying to allocate a slice with capacity -1. There's nothing saying that VectorSelector should have matchers, and it's actually prepared to have zero matcher strings, so it's worth checking instead of panicking. Signed-off-by: Oleg Zaytsev --- promql/parser/printer.go | 5 ++- promql/parser/printer_test.go | 75 +++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/promql/parser/printer.go b/promql/parser/printer.go index c5f80eb0b..f5cfe789b 100644 --- a/promql/parser/printer.go +++ b/promql/parser/printer.go @@ -182,7 +182,10 @@ func (node *UnaryExpr) String() string { } func (node *VectorSelector) String() string { - labelStrings := make([]string, 0, len(node.LabelMatchers)-1) + var labelStrings []string + if len(node.LabelMatchers) > 1 { + labelStrings = make([]string, 0, len(node.LabelMatchers)-1) + } for _, matcher := range node.LabelMatchers { // Only include the __name__ label if its equality matching and matches the name. if matcher.Name == labels.MetricName && matcher.Type == labels.MatchEqual && matcher.Value == node.Name { diff --git a/promql/parser/printer_test.go b/promql/parser/printer_test.go index 1ad48c3d4..e687820c9 100644 --- a/promql/parser/printer_test.go +++ b/promql/parser/printer_test.go @@ -16,6 +16,8 @@ package parser import ( "testing" + "github.com/prometheus/prometheus/pkg/labels" + "github.com/stretchr/testify/require" ) @@ -138,3 +140,76 @@ func TestExprString(t *testing.T) { require.Equal(t, exp, expr.String()) } } + +func TestVectorSelector_String(t *testing.T) { + for _, tc := range []struct { + name string + vs VectorSelector + expected string + }{ + { + name: "empty value", + vs: VectorSelector{}, + expected: ``, + }, + { + name: "no matchers with name", + vs: VectorSelector{Name: "foobar"}, + expected: `foobar`, + }, + { + name: "one matcher with name", + vs: VectorSelector{ + Name: "foobar", + LabelMatchers: []*labels.Matcher{ + labels.MustNewMatcher(labels.MatchEqual, "a", "x"), + }, + }, + expected: `foobar{a="x"}`, + }, + { + name: "two matchers with name", + vs: VectorSelector{ + Name: "foobar", + LabelMatchers: []*labels.Matcher{ + labels.MustNewMatcher(labels.MatchEqual, "a", "x"), + labels.MustNewMatcher(labels.MatchEqual, "b", "y"), + }, + }, + expected: `foobar{a="x",b="y"}`, + }, + { + name: "two matchers without name", + vs: VectorSelector{ + LabelMatchers: []*labels.Matcher{ + labels.MustNewMatcher(labels.MatchEqual, "a", "x"), + labels.MustNewMatcher(labels.MatchEqual, "b", "y"), + }, + }, + expected: `{a="x",b="y"}`, + }, + { + name: "name matcher and name", + vs: VectorSelector{ + Name: "foobar", + LabelMatchers: []*labels.Matcher{ + labels.MustNewMatcher(labels.MatchEqual, labels.MetricName, "foobar"), + }, + }, + expected: `foobar`, + }, + { + name: "name matcher only", + vs: VectorSelector{ + LabelMatchers: []*labels.Matcher{ + labels.MustNewMatcher(labels.MatchEqual, labels.MetricName, "foobar"), + }, + }, + expected: `{__name__="foobar"}`, + }, + } { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expected, tc.vs.String()) + }) + } +}