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 <mail@olegzaytsev.com>
This commit is contained in:
Oleg Zaytsev 2021-09-01 09:48:18 +02:00 committed by GitHub
parent e8be1d0a5c
commit 0a43e788af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 79 additions and 1 deletions

View File

@ -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 {

View File

@ -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())
})
}
}