Add lowercase and uppercase relabel action (#10641)

Signed-off-by: Julien Pivotto <roidelapluie@o11y.eu>
This commit is contained in:
Julien Pivotto 2022-05-03 10:09:53 +02:00 committed by GitHub
parent 4e6374180d
commit 71dbb4d091
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 113 additions and 3 deletions

View file

@ -858,6 +858,17 @@ var expectedConf = &Config{
Scheme: DefaultScrapeConfig.Scheme,
HTTPClientConfig: config.DefaultHTTPClientConfig,
RelabelConfigs: []*relabel.Config{
{
Action: relabel.Uppercase,
Regex: relabel.DefaultRelabelConfig.Regex,
Replacement: relabel.DefaultRelabelConfig.Replacement,
Separator: relabel.DefaultRelabelConfig.Separator,
SourceLabels: model.LabelNames{"instance"},
TargetLabel: "instance",
},
},
ServiceDiscoveryConfigs: discovery.Configs{
&hetzner.SDConfig{
HTTPClientConfig: config.HTTPClientConfig{
@ -1197,6 +1208,30 @@ var expectedErrors = []struct {
filename: "labelmap.bad.yml",
errMsg: "\"l-$1\" is invalid 'replacement' for labelmap action",
},
{
filename: "lowercase.bad.yml",
errMsg: "relabel configuration for lowercase action requires 'target_label' value",
},
{
filename: "lowercase2.bad.yml",
errMsg: "\"42lab\" is invalid 'target_label' for lowercase action",
},
{
filename: "lowercase3.bad.yml",
errMsg: "'replacement' can not be set for lowercase action",
},
{
filename: "uppercase.bad.yml",
errMsg: "relabel configuration for uppercase action requires 'target_label' value",
},
{
filename: "uppercase2.bad.yml",
errMsg: "\"42lab\" is invalid 'target_label' for uppercase action",
},
{
filename: "uppercase3.bad.yml",
errMsg: "'replacement' can not be set for uppercase action",
},
{
filename: "rules.bad.yml",
errMsg: "invalid rule file path",

View file

@ -328,6 +328,10 @@ scrape_configs:
key_file: valid_key_file
- job_name: hetzner
relabel_configs:
- action: uppercase
source_labels: [instance]
target_label: instance
hetzner_sd_configs:
- role: hcloud
authorization:

5
config/testdata/lowercase.bad.yml vendored Normal file
View file

@ -0,0 +1,5 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- action: lowercase
source_labels: [__name__]

6
config/testdata/lowercase2.bad.yml vendored Normal file
View file

@ -0,0 +1,6 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- action: lowercase
source_labels: [__name__]
target_label: 42lab

7
config/testdata/lowercase3.bad.yml vendored Normal file
View file

@ -0,0 +1,7 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- action: lowercase
source_labels: [__name__]
target_label: __name__
replacement: bar

5
config/testdata/uppercase.bad.yml vendored Normal file
View file

@ -0,0 +1,5 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- action: uppercase
source_labels: [__name__]

6
config/testdata/uppercase2.bad.yml vendored Normal file
View file

@ -0,0 +1,6 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- action: uppercase
source_labels: [__name__]
target_label: 42lab

7
config/testdata/uppercase3.bad.yml vendored Normal file
View file

@ -0,0 +1,7 @@
scrape_configs:
- job_name: prometheus
relabel_configs:
- action: uppercase
source_labels: [__name__]
target_label: __name__
replacement: bar

View file

@ -2536,6 +2536,8 @@ anchored on both ends. To un-anchor the regex, use `.*<regex>.*`.
`target_label` to `replacement`, with match group references
(`${1}`, `${2}`, ...) in `replacement` substituted by their value. If `regex`
does not match, no replacement takes place.
* `lowercase`: Maps the concatenated `source_labels` to their lower case.
* `uppercase`: Maps the concatenated `source_labels` to their upper case.
* `keep`: Drop targets for which `regex` does not match the concatenated `source_labels`.
* `drop`: Drop targets for which `regex` matches the concatenated `source_labels`.
* `hashmod`: Set `target_label` to the `modulus` of a hash of the concatenated `source_labels`.

View file

@ -54,6 +54,10 @@ const (
LabelDrop Action = "labeldrop"
// LabelKeep drops any label not matching the regex.
LabelKeep Action = "labelkeep"
// Lowercase maps input letters to their lower case.
Lowercase Action = "lowercase"
// Uppercase maps input letters to their upper case.
Uppercase Action = "uppercase"
)
// UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -63,7 +67,7 @@ func (a *Action) UnmarshalYAML(unmarshal func(interface{}) error) error {
return err
}
switch act := Action(strings.ToLower(s)); act {
case Replace, Keep, Drop, HashMod, LabelMap, LabelDrop, LabelKeep:
case Replace, Keep, Drop, HashMod, LabelMap, LabelDrop, LabelKeep, Lowercase, Uppercase:
*a = act
return nil
}
@ -106,12 +110,15 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
if c.Modulus == 0 && c.Action == HashMod {
return errors.Errorf("relabel configuration for hashmod requires non-zero modulus")
}
if (c.Action == Replace || c.Action == HashMod) && c.TargetLabel == "" {
if (c.Action == Replace || c.Action == HashMod || c.Action == Lowercase || c.Action == Uppercase) && c.TargetLabel == "" {
return errors.Errorf("relabel configuration for %s action requires 'target_label' value", c.Action)
}
if c.Action == Replace && !relabelTarget.MatchString(c.TargetLabel) {
if (c.Action == Replace || c.Action == Lowercase || c.Action == Uppercase) && !relabelTarget.MatchString(c.TargetLabel) {
return errors.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action)
}
if (c.Action == Lowercase || c.Action == Uppercase) && c.Replacement != DefaultRelabelConfig.Replacement {
return errors.Errorf("'replacement' can not be set for %s action", c.Action)
}
if c.Action == LabelMap && !relabelTarget.MatchString(c.Replacement) {
return errors.Errorf("%q is invalid 'replacement' for %s action", c.Replacement, c.Action)
}
@ -228,6 +235,10 @@ func relabel(lset labels.Labels, cfg *Config) labels.Labels {
break
}
lb.Set(string(target), string(res))
case Lowercase:
lb.Set(cfg.TargetLabel, strings.ToLower(val))
case Uppercase:
lb.Set(cfg.TargetLabel, strings.ToUpper(val))
case HashMod:
mod := sum64(md5.Sum([]byte(val))) % cfg.Modulus
lb.Set(cfg.TargetLabel, fmt.Sprintf("%d", mod))

View file

@ -428,6 +428,28 @@ func TestRelabel(t *testing.T) {
"a": "foo",
}),
},
{
input: labels.FromMap(map[string]string{
"foo": "bAr123Foo",
}),
relabel: []*Config{
{
SourceLabels: model.LabelNames{"foo"},
Action: Uppercase,
TargetLabel: "foo_uppercase",
},
{
SourceLabels: model.LabelNames{"foo"},
Action: Lowercase,
TargetLabel: "foo_lowercase",
},
},
output: labels.FromMap(map[string]string{
"foo": "bAr123Foo",
"foo_lowercase": "bar123foo",
"foo_uppercase": "BAR123FOO",
}),
},
}
for _, test := range tests {