mirror of
https://github.com/prometheus/prometheus.git
synced 2025-03-05 20:59:13 -08:00
Fixed relabelling; allowing UTF-8 in targetLabel.
Signed-off-by: bwplotka <bwplotka@gmail.com>
This commit is contained in:
parent
de00608036
commit
7263dfe50e
|
@ -29,7 +29,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
relabelTarget = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$(?:\{\w+\}|\w+))+\w*)+$`)
|
relabelTargetLegacy = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$(?:\{\w+\}|\w+))+\w*)+$`)
|
||||||
|
relabelTargetUTF8 = regexp.MustCompile(`^(?:(?:[^\${}]|\$(?:\{[^\${}]+\}|[^\${}]+))+[^\${}]*)+$`) // All UTF-8 chars, but ${}.
|
||||||
|
|
||||||
DefaultRelabelConfig = Config{
|
DefaultRelabelConfig = Config{
|
||||||
Action: Replace,
|
Action: Replace,
|
||||||
|
@ -124,10 +125,15 @@ func (c *Config) Validate() error {
|
||||||
if (c.Action == Replace || c.Action == HashMod || c.Action == Lowercase || c.Action == Uppercase || c.Action == KeepEqual || c.Action == DropEqual) && c.TargetLabel == "" {
|
if (c.Action == Replace || c.Action == HashMod || c.Action == Lowercase || c.Action == Uppercase || c.Action == KeepEqual || c.Action == DropEqual) && c.TargetLabel == "" {
|
||||||
return fmt.Errorf("relabel configuration for %s action requires 'target_label' value", c.Action)
|
return fmt.Errorf("relabel configuration for %s action requires 'target_label' value", c.Action)
|
||||||
}
|
}
|
||||||
if c.Action == Replace && !strings.Contains(c.TargetLabel, "$") && !model.LabelName(c.TargetLabel).IsValid() {
|
if c.Action == Replace && !varInRegexTemplate(c.TargetLabel) && !model.LabelName(c.TargetLabel).IsValid() {
|
||||||
return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action)
|
return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action)
|
||||||
}
|
}
|
||||||
if c.Action == Replace && strings.Contains(c.TargetLabel, "$") && !relabelTarget.MatchString(c.TargetLabel) {
|
|
||||||
|
relabelTargetRe := relabelTargetUTF8
|
||||||
|
if model.NameValidationScheme == model.LegacyValidation {
|
||||||
|
relabelTargetRe = relabelTargetLegacy
|
||||||
|
}
|
||||||
|
if c.Action == Replace && varInRegexTemplate(c.TargetLabel) && !relabelTargetRe.MatchString(c.TargetLabel) {
|
||||||
return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action)
|
return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action)
|
||||||
}
|
}
|
||||||
if (c.Action == Lowercase || c.Action == Uppercase || c.Action == KeepEqual || c.Action == DropEqual) && !model.LabelName(c.TargetLabel).IsValid() {
|
if (c.Action == Lowercase || c.Action == Uppercase || c.Action == KeepEqual || c.Action == DropEqual) && !model.LabelName(c.TargetLabel).IsValid() {
|
||||||
|
@ -136,7 +142,7 @@ func (c *Config) Validate() error {
|
||||||
if (c.Action == Lowercase || c.Action == Uppercase || c.Action == KeepEqual || c.Action == DropEqual) && c.Replacement != DefaultRelabelConfig.Replacement {
|
if (c.Action == Lowercase || c.Action == Uppercase || c.Action == KeepEqual || c.Action == DropEqual) && c.Replacement != DefaultRelabelConfig.Replacement {
|
||||||
return fmt.Errorf("'replacement' can not be set for %s action", c.Action)
|
return fmt.Errorf("'replacement' can not be set for %s action", c.Action)
|
||||||
}
|
}
|
||||||
if c.Action == LabelMap && !relabelTarget.MatchString(c.Replacement) {
|
if c.Action == LabelMap && !relabelTargetRe.MatchString(c.Replacement) {
|
||||||
return fmt.Errorf("%q is invalid 'replacement' for %s action", c.Replacement, c.Action)
|
return fmt.Errorf("%q is invalid 'replacement' for %s action", c.Replacement, c.Action)
|
||||||
}
|
}
|
||||||
if c.Action == HashMod && !model.LabelName(c.TargetLabel).IsValid() {
|
if c.Action == HashMod && !model.LabelName(c.TargetLabel).IsValid() {
|
||||||
|
|
|
@ -593,6 +593,75 @@ func TestRelabel(t *testing.T) {
|
||||||
"d": "match",
|
"d": "match",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
{ // Replace on source label with UTF-8 characters.
|
||||||
|
input: labels.FromMap(map[string]string{
|
||||||
|
"utf-8.label": "line1\nline2",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "baz",
|
||||||
|
}),
|
||||||
|
relabel: []*Config{
|
||||||
|
{
|
||||||
|
SourceLabels: model.LabelNames{"utf-8.label"},
|
||||||
|
Regex: MustNewRegexp("line1.*line2"),
|
||||||
|
TargetLabel: "d",
|
||||||
|
Separator: ";",
|
||||||
|
Replacement: `match${1}`,
|
||||||
|
Action: Replace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: labels.FromMap(map[string]string{
|
||||||
|
"utf-8.label": "line1\nline2",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "baz",
|
||||||
|
"d": "match",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{ // Replace targetLabel with UTF-8 characters.
|
||||||
|
input: labels.FromMap(map[string]string{
|
||||||
|
"a": "line1\nline2",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "baz",
|
||||||
|
}),
|
||||||
|
relabel: []*Config{
|
||||||
|
{
|
||||||
|
SourceLabels: model.LabelNames{"a"},
|
||||||
|
Regex: MustNewRegexp("line1.*line2"),
|
||||||
|
TargetLabel: "utf-8.label",
|
||||||
|
Separator: ";",
|
||||||
|
Replacement: `match${1}`,
|
||||||
|
Action: Replace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: labels.FromMap(map[string]string{
|
||||||
|
"a": "line1\nline2",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "baz",
|
||||||
|
"utf-8.label": "match",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{ // Replace targetLabel with UTF-8 characters and $variable.
|
||||||
|
input: labels.FromMap(map[string]string{
|
||||||
|
"a": "line1\nline2",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "baz",
|
||||||
|
}),
|
||||||
|
relabel: []*Config{
|
||||||
|
{
|
||||||
|
SourceLabels: model.LabelNames{"a"},
|
||||||
|
Regex: MustNewRegexp("line1.*line2"),
|
||||||
|
TargetLabel: "utf-8.label${1}",
|
||||||
|
Separator: ";",
|
||||||
|
Replacement: `match${1}`,
|
||||||
|
Action: Replace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: labels.FromMap(map[string]string{
|
||||||
|
"a": "line1\nline2",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "baz",
|
||||||
|
"utf-8.label": "match",
|
||||||
|
}),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
@ -646,9 +715,8 @@ func TestRelabelValidate(t *testing.T) {
|
||||||
config: Config{
|
config: Config{
|
||||||
Action: Lowercase,
|
Action: Lowercase,
|
||||||
Replacement: DefaultRelabelConfig.Replacement,
|
Replacement: DefaultRelabelConfig.Replacement,
|
||||||
TargetLabel: "${3}",
|
TargetLabel: "${3}", // With UTF-8 naming, this is now a legal relabel rule.
|
||||||
},
|
},
|
||||||
expected: `"${3}" is invalid 'target_label'`,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
config: Config{
|
config: Config{
|
||||||
|
@ -665,9 +733,8 @@ func TestRelabelValidate(t *testing.T) {
|
||||||
Regex: MustNewRegexp("some-([^-]+)-([^,]+)"),
|
Regex: MustNewRegexp("some-([^-]+)-([^,]+)"),
|
||||||
Action: Replace,
|
Action: Replace,
|
||||||
Replacement: "${1}",
|
Replacement: "${1}",
|
||||||
TargetLabel: "0${3}",
|
TargetLabel: "0${3}", // With UTF-8 naming this targets a valid label.
|
||||||
},
|
},
|
||||||
expected: `"0${3}" is invalid 'target_label'`,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
config: Config{
|
config: Config{
|
||||||
|
@ -675,9 +742,8 @@ func TestRelabelValidate(t *testing.T) {
|
||||||
Regex: MustNewRegexp("some-([^-]+)-([^,]+)"),
|
Regex: MustNewRegexp("some-([^-]+)-([^,]+)"),
|
||||||
Action: Replace,
|
Action: Replace,
|
||||||
Replacement: "${1}",
|
Replacement: "${1}",
|
||||||
TargetLabel: "-${3}",
|
TargetLabel: "-${3}", // With UTF-8 naming this targets a valid label.
|
||||||
},
|
},
|
||||||
expected: `"-${3}" is invalid 'target_label' for replace action`,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
|
@ -693,30 +759,66 @@ func TestRelabelValidate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTargetLabelValidity(t *testing.T) {
|
func TestTargetLabelValidity(t *testing.T) {
|
||||||
tests := []struct {
|
t.Run("legacy", func(t *testing.T) {
|
||||||
str string
|
for _, test := range []struct {
|
||||||
valid bool
|
str string
|
||||||
}{
|
valid bool
|
||||||
{"-label", false},
|
}{
|
||||||
{"label", true},
|
{"-label", false},
|
||||||
{"label${1}", true},
|
{"label", true},
|
||||||
{"${1}label", true},
|
{"label${1}", true},
|
||||||
{"${1}", true},
|
{"${1}label", true},
|
||||||
{"${1}label", true},
|
{"${1}", true},
|
||||||
{"${", false},
|
{"${1}label", true},
|
||||||
{"$", false},
|
{"${", false},
|
||||||
{"${}", false},
|
{"$", false},
|
||||||
{"foo${", false},
|
{"${}", false},
|
||||||
{"$1", true},
|
{"foo${", false},
|
||||||
{"asd$2asd", true},
|
{"$1", true},
|
||||||
{"-foo${1}bar-", false},
|
{"asd$2asd", true},
|
||||||
{"_${1}_", true},
|
{"-foo${1}bar-", false},
|
||||||
{"foo${bar}foo", true},
|
{"bar.foo${1}bar", false},
|
||||||
}
|
{"_${1}_", true},
|
||||||
for _, test := range tests {
|
{"foo${bar}foo", true},
|
||||||
require.Equal(t, test.valid, relabelTarget.Match([]byte(test.str)),
|
} {
|
||||||
"Expected %q to be %v", test.str, test.valid)
|
t.Run(test.str, func(t *testing.T) {
|
||||||
}
|
require.Equal(t, test.valid, relabelTargetLegacy.Match([]byte(test.str)),
|
||||||
|
"Expected %q to be %v", test.str, test.valid)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("utf-8", func(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
str string
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
{"-label", true},
|
||||||
|
{"label", true},
|
||||||
|
{"label${1}", true},
|
||||||
|
{"${1}label", true},
|
||||||
|
{"${1}", true},
|
||||||
|
{"${1}label", true},
|
||||||
|
{"$1", true},
|
||||||
|
{"asd$2asd", true},
|
||||||
|
{"-foo${1}bar-", true},
|
||||||
|
{"bar.foo${1}bar", true},
|
||||||
|
{"_${1}_", true},
|
||||||
|
{"foo${bar}foo", true},
|
||||||
|
|
||||||
|
// Those can be ambiguous. Currently, we assume user error.
|
||||||
|
{"${", false},
|
||||||
|
{"$", false},
|
||||||
|
{"${}", false},
|
||||||
|
{"foo${", false},
|
||||||
|
} {
|
||||||
|
t.Run(test.str, func(t *testing.T) {
|
||||||
|
require.Equal(t, test.valid, relabelTargetUTF8.Match([]byte(test.str)),
|
||||||
|
"Expected %q to be %v", test.str, test.valid)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkRelabel(b *testing.B) {
|
func BenchmarkRelabel(b *testing.B) {
|
||||||
|
|
Loading…
Reference in a new issue