VictoriaMetrics/lib/promrelabel/relabel_test.go

980 lines
23 KiB
Go

package promrelabel
import (
"fmt"
"reflect"
"testing"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
)
func TestSanitizeMetricName(t *testing.T) {
f := func(s, resultExpected string) {
t.Helper()
for i := 0; i < 5; i++ {
result := SanitizeMetricName(s)
if result != resultExpected {
t.Fatalf("unexpected result for SanitizeMetricName(%q) at iteration %d; got %q; want %q", s, i, result, resultExpected)
}
}
}
f("", "")
f("a", "a")
f("foo.bar/baz:a", "foo_bar_baz:a")
f("foo...bar", "foo___bar")
}
func TestSanitizeLabelName(t *testing.T) {
f := func(s, resultExpected string) {
t.Helper()
for i := 0; i < 5; i++ {
result := SanitizeLabelName(s)
if result != resultExpected {
t.Fatalf("unexpected result for SanitizeLabelName(%q) at iteration %d; got %q; want %q", s, i, result, resultExpected)
}
}
}
f("", "")
f("a", "a")
f("foo.bar/baz:a", "foo_bar_baz_a")
f("foo...bar", "foo___bar")
}
func TestLabelsToString(t *testing.T) {
f := func(labels []prompbmarshal.Label, sExpected string) {
t.Helper()
s := LabelsToString(labels)
if s != sExpected {
t.Fatalf("unexpected result;\ngot\n%s\nwant\n%s", s, sExpected)
}
}
f(nil, "{}")
f([]prompbmarshal.Label{
{
Name: "__name__",
Value: "foo",
},
}, "foo")
f([]prompbmarshal.Label{
{
Name: "foo",
Value: "bar",
},
}, `{foo="bar"}`)
f([]prompbmarshal.Label{
{
Name: "foo",
Value: "bar",
},
{
Name: "a",
Value: "bc",
},
}, `{a="bc",foo="bar"}`)
f([]prompbmarshal.Label{
{
Name: "foo",
Value: "bar",
},
{
Name: "__name__",
Value: "xxx",
},
{
Name: "a",
Value: "bc",
},
}, `xxx{a="bc",foo="bar"}`)
}
func TestParsedRelabelConfigsApplyDebug(t *testing.T) {
f := func(config, metric string, dssExpected []DebugStep) {
t.Helper()
pcs, err := ParseRelabelConfigsData([]byte(config))
if err != nil {
t.Fatalf("cannot parse %q: %s", config, err)
}
labels := promutils.MustNewLabelsFromString(metric)
_, dss := pcs.ApplyDebug(labels.GetLabels())
if !reflect.DeepEqual(dss, dssExpected) {
t.Fatalf("unexpected result; got\n%s\nwant\n%s", dss, dssExpected)
}
}
// empty relabel config
f(``, `foo`, nil)
// add label
f(`
- target_label: abc
replacement: xyz
`, `foo{bar="baz"}`, []DebugStep{
{
Rule: "target_label: abc\nreplacement: xyz\n",
In: `foo{bar="baz"}`,
Out: `foo{abc="xyz",bar="baz"}`,
},
})
// drop label
f(`
- target_label: bar
replacement: ''
`, `foo{bar="baz"}`, []DebugStep{
{
Rule: "target_label: bar\nreplacement: \"\"\n",
In: `foo{bar="baz"}`,
Out: `foo{bar=""}`,
},
{
Rule: "remove empty labels",
In: `foo{bar=""}`,
Out: `foo`,
},
})
// drop metric
f(`
- action: drop
source_labels: [bar]
regex: baz
`, `foo{bar="baz",abc="def"}`, []DebugStep{
{
Rule: "action: drop\nsource_labels: [bar]\nregex: baz\n",
In: `foo{abc="def",bar="baz"}`,
Out: `{}`,
},
})
// Multiple steps
f(`
- action: labeldrop
regex: "foo.*"
- target_label: foobar
replacement: "abc"
`, `m{foo="x",foobc="123",a="b"}`, []DebugStep{
{
Rule: "action: labeldrop\nregex: foo.*\n",
In: `m{a="b",foo="x",foobc="123"}`,
Out: `m{a="b"}`,
},
{
Rule: "target_label: foobar\nreplacement: abc\n",
In: `m{a="b"}`,
Out: `m{a="b",foobar="abc"}`,
},
})
}
func TestParsedRelabelConfigsApply(t *testing.T) {
f := func(config, metric string, isFinalize bool, resultExpected string) {
t.Helper()
pcs, err := ParseRelabelConfigsData([]byte(config))
if err != nil {
t.Fatalf("cannot parse %q: %s", config, err)
}
labels := promutils.MustNewLabelsFromString(metric)
resultLabels := pcs.Apply(labels.GetLabels(), 0)
if isFinalize {
resultLabels = FinalizeLabels(resultLabels[:0], resultLabels)
}
SortLabels(resultLabels)
result := LabelsToString(resultLabels)
if result != resultExpected {
t.Fatalf("unexpected result; got\n%s\nwant\n%s", result, resultExpected)
}
}
t.Run("empty_relabel_configs", func(t *testing.T) {
f("", `{}`, false, `{}`)
f("", `{}`, true, `{}`)
f("", `{foo="bar"}`, false, `{foo="bar"}`)
f("", `xxx{foo="bar",__aaa="yyy"}`, false, `xxx{__aaa="yyy",foo="bar"}`)
f("", `xxx{foo="bar",__aaa="yyy"}`, true, `xxx{foo="bar"}`)
})
t.Run("replace-miss", func(t *testing.T) {
f(`
- action: replace
target_label: bar
`, `{}`, false, `{}`)
f(`
- action: replace
source_labels: ["foo"]
target_label: bar
`, `{}`, false, `{}`)
f(`
- action: replace
source_labels: ["foo"]
target_label: "bar"
`, `{xxx="yyy"}`, false, `{xxx="yyy"}`)
f(`
- action: replace
source_labels: ["foo"]
target_label: "bar"
regex: ".+"
`, `{xxx="yyy"}`, false, `{xxx="yyy"}`)
f(`
- action: replace
source_labels: ["foo"]
target_label: "xxx"
regex: ".+"
`, `{xxx="yyy"}`, false, `{xxx="yyy"}`)
})
t.Run("replace-if-miss", func(t *testing.T) {
f(`
- action: replace
if: '{foo="bar"}'
source_labels: ["xxx", "foo"]
target_label: "bar"
replacement: "a-$1-b"
`, `{xxx="yyy"}`, false, `{xxx="yyy"}`)
})
t.Run("replace-hit", func(t *testing.T) {
f(`
- action: replace
source_labels: ["xxx", "foo"]
target_label: "bar"
replacement: "a-$1-b"
`, `{xxx="yyy"}`, false, `{bar="a-yyy;-b",xxx="yyy"}`)
f(`
- action: replace
source_labels: ["xxx", "foo"]
target_label: "xxx"
`, `{xxx="yyy"}`, false, `{xxx="yyy;"}`)
f(`
- action: replace
source_labels: ["foo"]
target_label: "xxx"
`, `{xxx="yyy"}`, false, `{}`)
})
t.Run("replace-if-hit", func(t *testing.T) {
f(`
- action: replace
if: '{xxx=~".y."}'
source_labels: ["xxx", "foo"]
target_label: "bar"
replacement: "a-$1-b"
`, `{xxx="yyy"}`, false, `{bar="a-yyy;-b",xxx="yyy"}`)
})
t.Run("replace-remove-label-value-hit", func(t *testing.T) {
f(`
- action: replace
source_labels: ["foo"]
target_label: "foo"
regex: "xxx"
replacement: ""
`, `{foo="xxx",bar="baz"}`, false, `{bar="baz"}`)
})
t.Run("replace-remove-label-value-miss", func(t *testing.T) {
f(`
- action: replace
source_labels: ["foo"]
target_label: "foo"
regex: "xxx"
replacement: ""
`, `{foo="yyy",bar="baz"}`, false, `{bar="baz",foo="yyy"}`)
})
t.Run("replace-hit-remove-label", func(t *testing.T) {
f(`
- action: replace
source_labels: ["xxx", "foo"]
regex: "yyy;.+"
target_label: "foo"
replacement: ""
`, `{xxx="yyy",foo="bar"}`, false, `{xxx="yyy"}`)
})
t.Run("replace-miss-remove-label", func(t *testing.T) {
f(`
- action: replace
source_labels: ["xxx", "foo"]
regex: "yyy;.+"
target_label: "foo"
replacement: ""
`, `{xxx="yyyz",foo="bar"}`, false, `{foo="bar",xxx="yyyz"}`)
})
t.Run("replace-hit-target-label-with-capture-group", func(t *testing.T) {
f(`
- action: replace
source_labels: ["xxx", "foo"]
target_label: "bar-$1"
replacement: "a-$1-b"
`, `{xxx="yyy"}`, false, `{bar-yyy;="a-yyy;-b",xxx="yyy"}`)
})
t.Run("replace_all-miss", func(t *testing.T) {
f(`
- action: replace_all
source_labels: [foo]
target_label: "bar"
`, `{}`, false, `{}`)
f(`
- action: replace_all
source_labels: ["foo"]
target_label: "bar"
`, `{}`, false, `{}`)
f(`
- action: replace_all
source_labels: ["foo"]
target_label: "bar"
`, `{xxx="yyy"}`, false, `{xxx="yyy"}`)
f(`
- action: replace_all
source_labels: ["foo"]
target_label: "bar"
regex: ".+"
`, `{xxx="yyy"}`, false, `{xxx="yyy"}`)
})
t.Run("replace_all-if-miss", func(t *testing.T) {
f(`
- action: replace_all
if: 'foo'
source_labels: ["xxx"]
target_label: "xxx"
regex: "-"
replacement: "."
`, `{xxx="a-b-c"}`, false, `{xxx="a-b-c"}`)
})
t.Run("replace_all-hit", func(t *testing.T) {
f(`
- action: replace_all
source_labels: ["xxx"]
target_label: "xxx"
regex: "-"
replacement: "."
`, `{xxx="a-b-c"}`, false, `{xxx="a.b.c"}`)
})
t.Run("replace_all-if-hit", func(t *testing.T) {
f(`
- action: replace_all
if: '{non_existing_label=~".*"}'
source_labels: ["xxx"]
target_label: "xxx"
regex: "-"
replacement: "."
`, `{xxx="a-b-c"}`, false, `{xxx="a.b.c"}`)
})
t.Run("replace_all-regex-hit", func(t *testing.T) {
f(`
- action: replace_all
source_labels: ["xxx", "foo"]
target_label: "xxx"
regex: "(;)"
replacement: "-$1-"
`, `{xxx="y;y"}`, false, `{xxx="y-;-y-;-"}`)
})
t.Run("replace-add-multi-labels", func(t *testing.T) {
f(`
- action: replace
source_labels: ["xxx"]
target_label: "bar"
replacement: "a-$1"
- action: replace
source_labels: ["bar"]
target_label: "zar"
replacement: "b-$1"
`, `{xxx="yyy",instance="a.bc"}`, true, `{bar="a-yyy",instance="a.bc",xxx="yyy",zar="b-a-yyy"}`)
})
t.Run("replace-self", func(t *testing.T) {
f(`
- action: replace
source_labels: ["foo"]
target_label: "foo"
replacement: "a-$1"
`, `{foo="aaxx"}`, true, `{foo="a-aaxx"}`)
})
t.Run("replace-missing-source", func(t *testing.T) {
f(`
- action: replace
target_label: foo
replacement: "foobar"
`, `{}`, true, `{foo="foobar"}`)
})
t.Run("keep_if_equal-miss", func(t *testing.T) {
f(`
- action: keep_if_equal
source_labels: ["foo", "bar"]
`, `{}`, true, `{}`)
f(`
- action: keep_if_equal
source_labels: ["xxx", "bar"]
`, `{xxx="yyy"}`, true, `{}`)
})
t.Run("keep_if_equal-hit", func(t *testing.T) {
f(`
- action: keep_if_equal
source_labels: ["xxx", "bar"]
`, `{xxx="yyy",bar="yyy"}`, true, `{bar="yyy",xxx="yyy"}`)
})
t.Run("drop_if_equal-miss", func(t *testing.T) {
f(`
- action: drop_if_equal
source_labels: ["foo", "bar"]
`, `{}`, true, `{}`)
f(`
- action: drop_if_equal
source_labels: ["xxx", "bar"]
`, `{xxx="yyy"}`, true, `{xxx="yyy"}`)
})
t.Run("drop_if_equal-hit", func(t *testing.T) {
f(`
- action: drop_if_equal
source_labels: [xxx, bar]
`, `{xxx="yyy",bar="yyy"}`, true, `{}`)
})
t.Run("keepequal-hit", func(t *testing.T) {
f(`
- action: keepequal
source_labels: [foo]
target_label: bar
`, `{foo="a",bar="a"}`, true, `{bar="a",foo="a"}`)
})
t.Run("keepequal-miss", func(t *testing.T) {
f(`
- action: keepequal
source_labels: [foo]
target_label: bar
`, `{foo="a",bar="x"}`, true, `{}`)
})
t.Run("dropequal-hit", func(t *testing.T) {
f(`
- action: dropequal
source_labels: [foo]
target_label: bar
`, `{foo="a",bar="a"}`, true, `{}`)
})
t.Run("dropequal-miss", func(t *testing.T) {
f(`
- action: dropequal
source_labels: [foo]
target_label: bar
`, `{foo="a",bar="x"}`, true, `{bar="x",foo="a"}`)
})
t.Run("keep-miss", func(t *testing.T) {
f(`
- action: keep
source_labels: [foo]
regex: ".+"
`, `{}`, true, `{}`)
f(`
- action: keep
source_labels: [foo]
regex: ".+"
`, `{xxx="yyy"}`, true, `{}`)
})
t.Run("keep-if-miss", func(t *testing.T) {
f(`
- action: keep
if: '{foo="bar"}'
`, `{foo="yyy"}`, false, `{}`)
})
t.Run("keep-if-hit", func(t *testing.T) {
f(`
- action: keep
if: ['foobar', '{foo="yyy"}', '{a="b"}']
`, `{foo="yyy"}`, false, `{foo="yyy"}`)
})
t.Run("keep-hit", func(t *testing.T) {
f(`
- action: keep
source_labels: [foo]
regex: "yyy"
`, `{foo="yyy"}`, false, `{foo="yyy"}`)
})
t.Run("keep-hit-regexp", func(t *testing.T) {
f(`
- action: keep
source_labels: ["foo"]
regex: ".+"
`, `{foo="yyy"}`, false, `{foo="yyy"}`)
})
t.Run("keep_metrics-miss", func(t *testing.T) {
f(`
- action: keep_metrics
regex:
- foo
- bar
`, `xxx`, true, `{}`)
})
t.Run("keep_metrics-if-miss", func(t *testing.T) {
f(`
- action: keep_metrics
if: 'bar'
`, `foo`, true, `{}`)
})
t.Run("keep_metrics-if-hit", func(t *testing.T) {
f(`
- action: keep_metrics
if: 'foo'
`, `foo`, true, `foo`)
})
t.Run("keep_metrics-hit", func(t *testing.T) {
f(`
- action: keep_metrics
regex:
- foo
- bar
`, `foo`, true, `foo`)
})
t.Run("drop-miss", func(t *testing.T) {
f(`
- action: drop
source_labels: [foo]
regex: ".+"
`, `{}`, false, `{}`)
f(`
- action: drop
source_labels: [foo]
regex: ".+"
`, `{xxx="yyy"}`, true, `{xxx="yyy"}`)
})
t.Run("drop-if-miss", func(t *testing.T) {
f(`
- action: drop
if: '{foo="bar"}'
`, `{foo="yyy"}`, true, `{foo="yyy"}`)
})
t.Run("drop-if-hit", func(t *testing.T) {
f(`
- action: drop
if: '{foo="yyy"}'
`, `{foo="yyy"}`, true, `{}`)
})
t.Run("drop-hit", func(t *testing.T) {
f(`
- action: drop
source_labels: [foo]
regex: yyy
`, `{foo="yyy"}`, true, `{}`)
})
t.Run("drop-hit-regexp", func(t *testing.T) {
f(`
- action: drop
source_labels: [foo]
regex: ".+"
`, `{foo="yyy"}`, true, `{}`)
})
t.Run("drop_metrics-miss", func(t *testing.T) {
f(`
- action: drop_metrics
regex:
- foo
- bar
`, `xxx`, true, `xxx`)
})
t.Run("drop_metrics-if-miss", func(t *testing.T) {
f(`
- action: drop_metrics
if: bar
`, `foo`, true, `foo`)
})
t.Run("drop_metrics-if-hit", func(t *testing.T) {
f(`
- action: drop_metrics
if: foo
`, `foo`, true, `{}`)
})
t.Run("drop_metrics-hit", func(t *testing.T) {
f(`
- action: drop_metrics
regex:
- foo
- bar
`, `foo`, true, `{}`)
})
t.Run("hashmod-miss", func(t *testing.T) {
f(`
- action: hashmod
source_labels: [foo]
target_label: aaa
modulus: 123
`, `{xxx="yyy"}`, false, `{aaa="81",xxx="yyy"}`)
})
t.Run("hashmod-if-miss", func(t *testing.T) {
f(`
- action: hashmod
if: '{foo="bar"}'
source_labels: [foo]
target_label: aaa
modulus: 123
`, `{foo="yyy"}`, true, `{foo="yyy"}`)
})
t.Run("hashmod-if-hit", func(t *testing.T) {
f(`
- action: hashmod
if: '{foo="yyy"}'
source_labels: [foo]
target_label: aaa
modulus: 123
`, `{foo="yyy"}`, true, `{aaa="73",foo="yyy"}`)
})
t.Run("hashmod-hit", func(t *testing.T) {
f(`
- action: hashmod
source_labels: [foo]
target_label: aaa
modulus: 123
`, `{foo="yyy"}`, true, `{aaa="73",foo="yyy"}`)
})
t.Run("labelmap-copy-label-if-miss", func(t *testing.T) {
f(`
- action: labelmap
if: '{foo="yyy",foobar="aab"}'
regex: "foo"
replacement: "bar"
`, `{foo="yyy",foobar="aaa"}`, true, `{foo="yyy",foobar="aaa"}`)
})
t.Run("labelmap-copy-label-if-hit", func(t *testing.T) {
f(`
- action: labelmap
if: '{foo="yyy",foobar="aaa"}'
regex: "foo"
replacement: "bar"
`, `{foo="yyy",foobar="aaa"}`, true, `{bar="yyy",foo="yyy",foobar="aaa"}`)
})
t.Run("labelmap-copy-label", func(t *testing.T) {
f(`
- action: labelmap
regex: "foo"
replacement: "bar"
`, `{foo="yyy",foobar="aaa"}`, true, `{bar="yyy",foo="yyy",foobar="aaa"}`)
})
t.Run("labelmap-remove-prefix-dot-star", func(t *testing.T) {
f(`
- action: labelmap
regex: "foo(.*)"
`, `{xoo="yyy",foobar="aaa"}`, true, `{bar="aaa",foobar="aaa",xoo="yyy"}`)
})
t.Run("labelmap-remove-prefix-dot-plus", func(t *testing.T) {
f(`
- action: labelmap
regex: "foo(.+)"
`, `{foo="yyy",foobar="aaa"}`, true, `{bar="aaa",foo="yyy",foobar="aaa"}`)
})
t.Run("labelmap-regex", func(t *testing.T) {
f(`
- action: labelmap
regex: "foo(.+)"
replacement: "$1-x"
`, `{foo="yyy",foobar="aaa"}`, true, `{bar-x="aaa",foo="yyy",foobar="aaa"}`)
})
t.Run("labelmap_all-if-miss", func(t *testing.T) {
f(`
- action: labelmap_all
if: foobar
regex: "\\."
replacement: "-"
`, `{foo.bar.baz="yyy",foobar="aaa"}`, true, `{foo.bar.baz="yyy",foobar="aaa"}`)
})
t.Run("labelmap_all-if-hit", func(t *testing.T) {
f(`
- action: labelmap_all
if: '{foo.bar.baz="yyy"}'
regex: "\\."
replacement: "-"
`, `{foo.bar.baz="yyy",foobar="aaa"}`, true, `{foo-bar-baz="yyy",foobar="aaa"}`)
})
t.Run("labelmap_all", func(t *testing.T) {
f(`
- action: labelmap_all
regex: "\\."
replacement: "-"
`, `{foo.bar.baz="yyy",foobar="aaa"}`, true, `{foo-bar-baz="yyy",foobar="aaa"}`)
})
t.Run("labelmap_all-regexp", func(t *testing.T) {
f(`
- action: labelmap_all
regex: "ba(.)"
replacement: "${1}ss"
`, `{foo.bar.baz="yyy",foozar="aaa"}`, true, `{foo.rss.zss="yyy",foozar="aaa"}`)
})
t.Run("labeldrop", func(t *testing.T) {
f(`
- action: labeldrop
regex: dropme
`, `{aaa="bbb"}`, true, `{aaa="bbb"}`)
// if-miss
f(`
- action: labeldrop
if: foo
regex: dropme
`, `{xxx="yyy",dropme="aaa",foo="bar"}`, false, `{dropme="aaa",foo="bar",xxx="yyy"}`)
// if-hit
f(`
- action: labeldrop
if: '{xxx="yyy"}'
regex: dropme
`, `{xxx="yyy",dropme="aaa",foo="bar"}`, false, `{foo="bar",xxx="yyy"}`)
f(`
- action: labeldrop
regex: dropme
`, `{xxx="yyy",dropme="aaa",foo="bar"}`, false, `{foo="bar",xxx="yyy"}`)
// regex in single quotes
f(`
- action: labeldrop
regex: 'dropme'
`, `{xxx="yyy",dropme="aaa"}`, false, `{xxx="yyy"}`)
// regex in double quotes
f(`
- action: labeldrop
regex: "dropme"
`, `{xxx="yyy",dropme="aaa"}`, false, `{xxx="yyy"}`)
})
t.Run("labeldrop-prefix", func(t *testing.T) {
f(`
- action: labeldrop
regex: "dropme.*"
`, `{aaa="bbb"}`, true, `{aaa="bbb"}`)
f(`
- action: labeldrop
regex: "dropme(.+)"
`, `{xxx="yyy",dropme-please="aaa",foo="bar"}`, false, `{foo="bar",xxx="yyy"}`)
})
t.Run("labeldrop-regexp", func(t *testing.T) {
f(`
- action: labeldrop
regex: ".*dropme.*"
`, `{aaa="bbb"}`, true, `{aaa="bbb"}`)
f(`
- action: labeldrop
regex: ".*dropme.*"
`, `{xxx="yyy",dropme-please="aaa",foo="bar"}`, false, `{foo="bar",xxx="yyy"}`)
})
t.Run("labelkeep", func(t *testing.T) {
f(`
- action: labelkeep
regex: "keepme"
`, `{keepme="aaa"}`, true, `{keepme="aaa"}`)
// if-miss
f(`
- action: labelkeep
if: '{aaaa="awefx"}'
regex: keepme
`, `{keepme="aaa",aaaa="awef",keepme-aaa="234"}`, false, `{aaaa="awef",keepme="aaa",keepme-aaa="234"}`)
// if-hit
f(`
- action: labelkeep
if: '{aaaa="awef"}'
regex: keepme
`, `{keepme="aaa",aaaa="awef",keepme-aaa="234"}`, false, `{keepme="aaa"}`)
f(`
- action: labelkeep
regex: keepme
`, `{keepme="aaa",aaaa="awef",keepme-aaa="234"}`, false, `{keepme="aaa"}`)
})
t.Run("labelkeep-regexp", func(t *testing.T) {
f(`
- action: labelkeep
regex: "keepme.*"
`, `{keepme="aaa"}`, true, `{keepme="aaa"}`)
f(`
- action: labelkeep
regex: "keepme.*"
`, `{keepme="aaa",aaaa="awef",keepme-aaa="234"}`, false, `{keepme="aaa",keepme-aaa="234"}`)
})
t.Run("upper-lower-case", func(t *testing.T) {
f(`
- action: uppercase
source_labels: ["foo"]
target_label: foo
`, `{foo="bar"}`, true, `{foo="BAR"}`)
f(`
- action: lowercase
source_labels: ["foo", "bar"]
target_label: baz
- action: labeldrop
regex: foo|bar
`, `{foo="BaR",bar="fOO"}`, true, `{baz="bar;foo"}`)
f(`
- action: lowercase
source_labels: ["foo"]
target_label: baz
- action: uppercase
source_labels: ["bar"]
target_label: baz
`, `{qux="quux"}`, true, `{qux="quux"}`)
})
t.Run("graphite-match", func(t *testing.T) {
f(`
- action: graphite
match: foo.*.baz
labels:
__name__: aaa
job: ${1}-zz
`, `foo.bar.baz`, true, `aaa{job="bar-zz"}`)
})
t.Run("graphite-mismatch", func(t *testing.T) {
f(`
- action: graphite
match: foo.*.baz
labels:
__name__: aaa
job: ${1}-zz
`, `foo.bar.bazz`, true, `foo.bar.bazz`)
})
t.Run("replacement-with-label-refs", func(t *testing.T) {
// no regex
f(`
- target_label: abc
replacement: "{{__name__}}.{{foo}}"
`, `qwe{foo="bar",baz="aaa"}`, true, `qwe{abc="qwe.bar",baz="aaa",foo="bar"}`)
// with regex
f(`
- target_label: abc
replacement: "{{__name__}}.{{foo}}.$1"
source_labels: [baz]
regex: "a(.+)"
`, `qwe{foo="bar",baz="aaa"}`, true, `qwe{abc="qwe.bar.aa",baz="aaa",foo="bar"}`)
})
// Check $ at the end of regex - see https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3131
t.Run("replacement-with-$-at-the-end-of-regex", func(t *testing.T) {
f(`
- target_label: xyz
regex: "foo\\$$"
replacement: bar
source_labels: [xyz]
`, `metric{xyz="foo$",a="b"}`, true, `metric{a="b",xyz="bar"}`)
})
t.Run("issue-3251", func(t *testing.T) {
f(`
- source_labels: [instance, container_label_com_docker_swarm_task_name]
separator: ';'
# regex: '(.*?)\..*;(.*?)\..*'
regex: '([^.]+).[^;]+;([^.]+).+'
replacement: '$2:$1'
target_label: container_label_com_docker_swarm_task_name
action: replace
`, `{instance="subdomain.domain.com",container_label_com_docker_swarm_task_name="myservice.h408nlaxmv8oqkn1pjjtd71to.nv987lz99rb27lkjjnfiay0g4"}`, true,
`{container_label_com_docker_swarm_task_name="myservice:subdomain",instance="subdomain.domain.com"}`)
})
}
func TestFinalizeLabels(t *testing.T) {
f := func(metric, resultExpected string) {
t.Helper()
labels := promutils.MustNewLabelsFromString(metric)
resultLabels := FinalizeLabels(nil, labels.GetLabels())
result := LabelsToString(resultLabels)
if result != resultExpected {
t.Fatalf("unexpected result; got\n%s\nwant\n%s", result, resultExpected)
}
}
f(`{}`, `{}`)
f(`{foo="bar",__aaa="ass",instance="foo.com"}`, `{foo="bar",instance="foo.com"}`)
f(`{foo="bar",instance="ass",__address__="foo.com"}`, `{foo="bar",instance="ass"}`)
f(`{foo="bar",abc="def",__address__="foo.com"}`, `{abc="def",foo="bar"}`)
}
func TestFillLabelReferences(t *testing.T) {
f := func(replacement, metric, resultExpected string) {
t.Helper()
labels := promutils.MustNewLabelsFromString(metric)
result := fillLabelReferences(nil, replacement, labels.GetLabels())
if string(result) != resultExpected {
t.Fatalf("unexpected result; got\n%q\nwant\n%q", result, resultExpected)
}
}
f(``, `foo{bar="baz"}`, ``)
f(`abc`, `foo{bar="baz"}`, `abc`)
f(`foo{{bar`, `foo{bar="baz"}`, `foo{{bar`)
f(`foo-$1`, `foo{bar="baz"}`, `foo-$1`)
f(`foo{{bar}}`, `foo{bar="baz"}`, `foobaz`)
f(`{{bar}}`, `foo{bar="baz"}`, `baz`)
f(`{{bar}}-aa`, `foo{bar="baz"}`, `baz-aa`)
f(`{{bar}}-aa{{__name__}}.{{bar}}{{non-existing-label}}`, `foo{bar="baz"}`, `baz-aafoo.baz`)
}
func TestRegexMatchStringSuccess(t *testing.T) {
f := func(pattern, s string) {
t.Helper()
prc := newTestRegexRelabelConfig(pattern)
if !prc.regex.MatchString(s) {
t.Fatalf("unexpected MatchString(%q) result; got false; want true", s)
}
}
f("", "")
f("foo", "foo")
f(".*", "")
f(".*", "foo")
f("foo.*", "foobar")
f("foo.+", "foobar")
f("f.+o", "foo")
f("foo|bar", "bar")
f("^(foo|bar)$", "foo")
f("foo.+", "foobar")
f("^foo$", "foo")
}
func TestRegexpMatchStringFailure(t *testing.T) {
f := func(pattern, s string) {
t.Helper()
prc := newTestRegexRelabelConfig(pattern)
if prc.regex.MatchString(s) {
t.Fatalf("unexpected MatchString(%q) result; got true; want false", s)
}
}
f("", "foo")
f("foo", "")
f("foo.*", "foa")
f("foo.+", "foo")
f("f.+o", "foor")
f("foo|bar", "barz")
f("^(foo|bar)$", "xfoo")
f("foo.+", "foo")
f("^foo$", "foobar")
}
func newTestRegexRelabelConfig(pattern string) *parsedRelabelConfig {
rc := &RelabelConfig{
Action: "labeldrop",
Regex: &MultiLineRegex{
S: pattern,
},
}
prc, err := parseRelabelConfig(rc)
if err != nil {
panic(fmt.Errorf("unexpected error in parseRelabelConfig: %s", err))
}
return prc
}
func TestParsedRelabelConfigsApplyForMultipleSeries(t *testing.T) {
f := func(config string, metrics []string, resultExpected []string) {
t.Helper()
pcs, err := ParseRelabelConfigsData([]byte(config))
if err != nil {
t.Fatalf("cannot parse %q: %s", config, err)
}
totalLabels := 0
var labels []prompbmarshal.Label
for _, metric := range metrics {
labels = append(labels, promutils.MustNewLabelsFromString(metric).GetLabels()...)
resultLabels := pcs.Apply(labels, totalLabels)
SortLabels(resultLabels)
totalLabels += len(resultLabels)
labels = resultLabels
}
var result []string
for i := range labels {
result = append(result, LabelsToString(labels[i:i+1]))
}
if len(result) != len(resultExpected) {
t.Fatalf("unexpected number of results; got\n%q\nwant\n%q", result, resultExpected)
}
for i := range result {
if result[i] != resultExpected[i] {
t.Fatalf("unexpected result[%d]; got\n%q\nwant\n%q", i, result[i], resultExpected[i])
}
}
}
t.Run("drops one of series", func(t *testing.T) {
f(`
- action: drop
if: '{__name__!~"smth"}'
`, []string{`smth`, `notthis`}, []string{`smth`})
f(`
- action: drop
if: '{__name__!~"smth"}'
`, []string{`notthis`, `smth`}, []string{`smth`})
})
}