diff --git a/config/config_test.go b/config/config_test.go index 1e30ab299..ddf65a068 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -217,26 +217,45 @@ var expectedConf = &Config{ Regex: relabel.MustNewRegexp("(.*)some-[regex]"), Replacement: "foo-${1}", Action: relabel.Replace, - }, { + }, + { SourceLabels: model.LabelNames{"abc"}, TargetLabel: "cde", Separator: ";", Regex: relabel.DefaultRelabelConfig.Regex, Replacement: relabel.DefaultRelabelConfig.Replacement, Action: relabel.Replace, - }, { + }, + { TargetLabel: "abc", Separator: ";", Regex: relabel.DefaultRelabelConfig.Regex, Replacement: "static", Action: relabel.Replace, - }, { + }, + { TargetLabel: "abc", Separator: ";", Regex: relabel.MustNewRegexp(""), Replacement: "static", Action: relabel.Replace, }, + { + SourceLabels: model.LabelNames{"foo"}, + TargetLabel: "abc", + Action: relabel.KeepEqual, + Regex: relabel.DefaultRelabelConfig.Regex, + Replacement: relabel.DefaultRelabelConfig.Replacement, + Separator: relabel.DefaultRelabelConfig.Separator, + }, + { + SourceLabels: model.LabelNames{"foo"}, + TargetLabel: "abc", + Action: relabel.DropEqual, + Regex: relabel.DefaultRelabelConfig.Regex, + Replacement: relabel.DefaultRelabelConfig.Replacement, + Separator: relabel.DefaultRelabelConfig.Separator, + }, }, }, { @@ -1316,6 +1335,22 @@ var expectedErrors = []struct { filename: "labeldrop5.bad.yml", errMsg: "labeldrop action requires only 'regex', and no other fields", }, + { + filename: "dropequal.bad.yml", + errMsg: "relabel configuration for dropequal action requires 'target_label' value", + }, + { + filename: "dropequal1.bad.yml", + errMsg: "dropequal action requires only 'source_labels' and `target_label`, and no other fields", + }, + { + filename: "keepequal.bad.yml", + errMsg: "relabel configuration for keepequal action requires 'target_label' value", + }, + { + filename: "keepequal1.bad.yml", + errMsg: "keepequal action requires only 'source_labels' and `target_label`, and no other fields", + }, { filename: "labelmap.bad.yml", errMsg: "\"l-$1\" is invalid 'replacement' for labelmap action", diff --git a/config/testdata/conf.good.yml b/config/testdata/conf.good.yml index da0042b94..764f1a342 100644 --- a/config/testdata/conf.good.yml +++ b/config/testdata/conf.good.yml @@ -87,6 +87,12 @@ scrape_configs: - regex: replacement: static target_label: abc + - source_labels: [foo] + target_label: abc + action: keepequal + - source_labels: [foo] + target_label: abc + action: dropequal authorization: credentials_file: valid_token_file diff --git a/config/testdata/dropequal.bad.yml b/config/testdata/dropequal.bad.yml new file mode 100644 index 000000000..32bd1a964 --- /dev/null +++ b/config/testdata/dropequal.bad.yml @@ -0,0 +1,5 @@ +scrape_configs: + - job_name: prometheus + relabel_configs: + - source_labels: [abcdef] + action: dropequal diff --git a/config/testdata/dropequal1.bad.yml b/config/testdata/dropequal1.bad.yml new file mode 100644 index 000000000..b648c0db2 --- /dev/null +++ b/config/testdata/dropequal1.bad.yml @@ -0,0 +1,7 @@ +scrape_configs: + - job_name: prometheus + relabel_configs: + - source_labels: [abcdef] + action: dropequal + regex: foo + target_label: bar diff --git a/config/testdata/keepequal.bad.yml b/config/testdata/keepequal.bad.yml new file mode 100644 index 000000000..5f5662177 --- /dev/null +++ b/config/testdata/keepequal.bad.yml @@ -0,0 +1,5 @@ +scrape_configs: + - job_name: prometheus + relabel_configs: + - source_labels: [abcdef] + action: keepequal diff --git a/config/testdata/keepequal1.bad.yml b/config/testdata/keepequal1.bad.yml new file mode 100644 index 000000000..c4628314c --- /dev/null +++ b/config/testdata/keepequal1.bad.yml @@ -0,0 +1,7 @@ +scrape_configs: + - job_name: prometheus + relabel_configs: + - source_labels: [abcdef] + action: keepequal + regex: foo + target_label: bar diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md index de495eaec..756e51d3f 100644 --- a/docs/configuration/configuration.md +++ b/docs/configuration/configuration.md @@ -2857,6 +2857,8 @@ anchored on both ends. To un-anchor the regex, use `.*.*`. * `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`. +* `keepequal`: Drop targets for which the concatenated `source_labels` do not match `target_label`. +* `dropequal`: Drop targets for which the concatenated `source_labels` do match `target_label`. * `hashmod`: Set `target_label` to the `modulus` of a hash of the concatenated `source_labels`. * `labelmap`: Match `regex` against all source label names, not just those specified in `source_labels`. Then copy the values of the matching labels to label names given by `replacement` with match diff --git a/model/relabel/relabel.go b/model/relabel/relabel.go index e0d7f6ddf..c731f6e0d 100644 --- a/model/relabel/relabel.go +++ b/model/relabel/relabel.go @@ -45,6 +45,10 @@ const ( Keep Action = "keep" // Drop drops targets for which the input does match the regex. Drop Action = "drop" + // KeepEqual drops targets for which the input does not match the target. + KeepEqual Action = "keepequal" + // Drop drops targets for which the input does match the target. + DropEqual Action = "dropequal" // HashMod sets a label to the modulus of a hash of labels. HashMod Action = "hashmod" // LabelMap copies labels to other labelnames based on a regex. @@ -66,7 +70,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, Lowercase, Uppercase: + case Replace, Keep, Drop, HashMod, LabelMap, LabelDrop, LabelKeep, Lowercase, Uppercase, KeepEqual, DropEqual: *a = act return nil } @@ -109,13 +113,13 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { if c.Modulus == 0 && c.Action == HashMod { return fmt.Errorf("relabel configuration for hashmod requires non-zero modulus") } - if (c.Action == Replace || c.Action == HashMod || c.Action == Lowercase || c.Action == Uppercase) && 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) } - if (c.Action == Replace || c.Action == Lowercase || c.Action == Uppercase) && !relabelTarget.MatchString(c.TargetLabel) { + if (c.Action == Replace || c.Action == Lowercase || c.Action == Uppercase || c.Action == KeepEqual || c.Action == DropEqual) && !relabelTarget.MatchString(c.TargetLabel) { return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action) } - if (c.Action == Lowercase || c.Action == Uppercase) && 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) } if c.Action == LabelMap && !relabelTarget.MatchString(c.Replacement) { @@ -125,6 +129,15 @@ func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action) } + if c.Action == DropEqual || c.Action == KeepEqual { + if c.Regex != DefaultRelabelConfig.Regex || + c.Modulus != DefaultRelabelConfig.Modulus || + c.Separator != DefaultRelabelConfig.Separator || + c.Replacement != DefaultRelabelConfig.Replacement { + return fmt.Errorf("%s action requires only 'source_labels' and `target_label`, and no other fields", c.Action) + } + } + if c.Action == LabelDrop || c.Action == LabelKeep { if c.SourceLabels != nil || c.TargetLabel != DefaultRelabelConfig.TargetLabel || @@ -225,6 +238,14 @@ func relabel(lset labels.Labels, cfg *Config, lb *labels.Builder) labels.Labels if !cfg.Regex.MatchString(val) { return nil } + case DropEqual: + if lset.Get(cfg.TargetLabel) == val { + return nil + } + case KeepEqual: + if lset.Get(cfg.TargetLabel) != val { + return nil + } case Replace: indexes := cfg.Regex.FindStringSubmatchIndex(val) // If there is no match no replacement must take place. diff --git a/model/relabel/relabel_test.go b/model/relabel/relabel_test.go index c437e5c1c..0b0dfd511 100644 --- a/model/relabel/relabel_test.go +++ b/model/relabel/relabel_test.go @@ -451,6 +451,74 @@ func TestRelabel(t *testing.T) { "foo_uppercase": "BAR123FOO", }), }, + { + input: labels.FromMap(map[string]string{ + "__tmp_port": "1234", + "__port1": "1234", + "__port2": "5678", + }), + relabel: []*Config{ + { + SourceLabels: model.LabelNames{"__tmp_port"}, + Action: KeepEqual, + TargetLabel: "__port1", + }, + }, + output: labels.FromMap(map[string]string{ + "__tmp_port": "1234", + "__port1": "1234", + "__port2": "5678", + }), + }, + { + input: labels.FromMap(map[string]string{ + "__tmp_port": "1234", + "__port1": "1234", + "__port2": "5678", + }), + relabel: []*Config{ + { + SourceLabels: model.LabelNames{"__tmp_port"}, + Action: DropEqual, + TargetLabel: "__port1", + }, + }, + output: nil, + }, + { + input: labels.FromMap(map[string]string{ + "__tmp_port": "1234", + "__port1": "1234", + "__port2": "5678", + }), + relabel: []*Config{ + { + SourceLabels: model.LabelNames{"__tmp_port"}, + Action: DropEqual, + TargetLabel: "__port2", + }, + }, + output: labels.FromMap(map[string]string{ + "__tmp_port": "1234", + "__port1": "1234", + "__port2": "5678", + }), + }, + { + input: labels.FromMap(map[string]string{ + "__tmp_port": "1234", + "__port1": "1234", + "__port2": "5678", + }), + relabel: []*Config{ + { + SourceLabels: model.LabelNames{"__tmp_port"}, + Action: KeepEqual, + TargetLabel: "__port2", + }, + }, + output: nil, + }, } for _, test := range tests {