From 5a1e909b5d8a63421556e98347d3cbeba4384d26 Mon Sep 17 00:00:00 2001 From: Matti Savolainen Date: Wed, 19 Oct 2016 00:33:22 +0300 Subject: [PATCH 1/6] Make TargetLabel in RelabelConfig a string --- config/config.go | 10 +++++-- config/testdata/relabel_target_label.good.yml | 0 relabel/relabel.go | 8 ++--- relabel/relabel_test.go | 30 +++++++++---------- 4 files changed, 27 insertions(+), 21 deletions(-) create mode 100644 config/testdata/relabel_target_label.good.yml diff --git a/config/config.go b/config/config.go index ee37ae126..8b13aeecf 100644 --- a/config/config.go +++ b/config/config.go @@ -31,6 +31,7 @@ var ( patFileSDName = regexp.MustCompile(`^[^*]*(\*[^/]*)?\.(json|yml|yaml|JSON|YML|YAML)$`) patRulePath = regexp.MustCompile(`^[^*]*(\*[^/]*)?$`) patAuthLine = regexp.MustCompile(`((?:password|bearer_token|secret_key|client_secret):\s+)(".+"|'.+'|[^\s]+)`) + relabelTarget = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$\{?[\w]+}?)+\w*)+$`) ) // Load parses the YAML input s into a Config. @@ -355,7 +356,6 @@ func (c *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { gc.EvaluationInterval = DefaultGlobalConfig.EvaluationInterval } *c = *gc - return nil } @@ -986,7 +986,7 @@ type RelabelConfig struct { // Modulus to take of the hash of concatenated values from the source labels. Modulus uint64 `yaml:"modulus,omitempty"` // The label to which the resulting string is written in a replacement. - TargetLabel model.LabelName `yaml:"target_label,omitempty"` + TargetLabel string `yaml:"target_label,omitempty"` // Replacement is the regex replacement pattern to be used. Replacement string `yaml:"replacement,omitempty"` // Action is the action to be performed for the relabeling. @@ -1012,6 +1012,12 @@ func (c *RelabelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if (c.Action == RelabelReplace || c.Action == RelabelHashMod) && c.TargetLabel == "" { return fmt.Errorf("relabel configuration for %s action requires 'target_label' value", c.Action) } + if c.Action == RelabelReplace && !relabelTarget.Match([]byte(c.TargetLabel)) { + return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action) + } + if c.Action == RelabelHashMod && !model.LabelNameRE.Match([]byte(c.TargetLabel)) { + return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action) + } return nil } diff --git a/config/testdata/relabel_target_label.good.yml b/config/testdata/relabel_target_label.good.yml new file mode 100644 index 000000000..e69de29bb diff --git a/relabel/relabel.go b/relabel/relabel.go index 6ce37296c..ff5904596 100644 --- a/relabel/relabel.go +++ b/relabel/relabel.go @@ -61,20 +61,20 @@ func relabel(labels model.LabelSet, cfg *config.RelabelConfig) model.LabelSet { if indexes == nil { break } - target := model.LabelName(cfg.Regex.ExpandString([]byte{}, string(cfg.TargetLabel), val, indexes)) + target := model.LabelName(cfg.Regex.ExpandString([]byte{}, cfg.TargetLabel, val, indexes)) if !target.IsValid() { - delete(labels, cfg.TargetLabel) + delete(labels, model.LabelName(cfg.TargetLabel)) break } res := cfg.Regex.ExpandString([]byte{}, cfg.Replacement, val, indexes) if len(res) == 0 { - delete(labels, cfg.TargetLabel) + delete(labels, model.LabelName(cfg.TargetLabel)) break } labels[target] = model.LabelValue(res) case config.RelabelHashMod: mod := sum64(md5.Sum([]byte(val))) % cfg.Modulus - labels[cfg.TargetLabel] = model.LabelValue(fmt.Sprintf("%d", mod)) + labels[model.LabelName(cfg.TargetLabel)] = model.LabelValue(fmt.Sprintf("%d", mod)) case config.RelabelLabelMap: out := make(model.LabelSet, len(labels)) // Take a copy to avoid infinite loops. diff --git a/relabel/relabel_test.go b/relabel/relabel_test.go index 4004fa96f..28fa6e901 100644 --- a/relabel/relabel_test.go +++ b/relabel/relabel_test.go @@ -38,7 +38,7 @@ func TestRelabel(t *testing.T) { { SourceLabels: model.LabelNames{"a"}, Regex: config.MustNewRegexp("f(.*)"), - TargetLabel: model.LabelName("d"), + TargetLabel: "d", Separator: ";", Replacement: "ch${1}-ch${1}", Action: config.RelabelReplace, @@ -61,7 +61,7 @@ func TestRelabel(t *testing.T) { { SourceLabels: model.LabelNames{"a", "b"}, Regex: config.MustNewRegexp("f(.*);(.*)r"), - TargetLabel: model.LabelName("a"), + TargetLabel: "a", Separator: ";", Replacement: "b${1}${2}m", // boobam Action: config.RelabelReplace, @@ -69,7 +69,7 @@ func TestRelabel(t *testing.T) { { SourceLabels: model.LabelNames{"c", "a"}, Regex: config.MustNewRegexp("(b).*b(.*)ba(.*)"), - TargetLabel: model.LabelName("d"), + TargetLabel: "d", Separator: ";", Replacement: "$1$2$2$3", Action: config.RelabelReplace, @@ -94,7 +94,7 @@ func TestRelabel(t *testing.T) { }, { SourceLabels: model.LabelNames{"a"}, Regex: config.MustNewRegexp("f(.*)"), - TargetLabel: model.LabelName("d"), + TargetLabel: "d", Separator: ";", Replacement: "ch$1-ch$1", Action: config.RelabelReplace, @@ -124,7 +124,7 @@ func TestRelabel(t *testing.T) { { SourceLabels: model.LabelNames{"a"}, Regex: config.MustNewRegexp(".*(b).*"), - TargetLabel: model.LabelName("d"), + TargetLabel: "d", Separator: ";", Replacement: "$1", Action: config.RelabelReplace, @@ -202,7 +202,7 @@ func TestRelabel(t *testing.T) { { SourceLabels: model.LabelNames{"a"}, Regex: config.MustNewRegexp("f"), - TargetLabel: model.LabelName("b"), + TargetLabel: "b", Replacement: "bar", Action: config.RelabelReplace, }, @@ -220,7 +220,7 @@ func TestRelabel(t *testing.T) { relabel: []*config.RelabelConfig{ { SourceLabels: model.LabelNames{"c"}, - TargetLabel: model.LabelName("d"), + TargetLabel: "d", Separator: ";", Action: config.RelabelHashMod, Modulus: 1000, @@ -287,7 +287,7 @@ func TestRelabel(t *testing.T) { Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"), Action: config.RelabelReplace, Replacement: "${2}", - TargetLabel: model.LabelName("${1}"), + TargetLabel: "${1}", }, }, output: model.LabelSet{ @@ -305,7 +305,7 @@ func TestRelabel(t *testing.T) { Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"), Action: config.RelabelReplace, Replacement: "${3}", - TargetLabel: model.LabelName("${1}"), + TargetLabel: "${1}", }, }, output: model.LabelSet{ @@ -322,21 +322,21 @@ func TestRelabel(t *testing.T) { Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"), Action: config.RelabelReplace, Replacement: "${1}", - TargetLabel: model.LabelName("${3}"), + TargetLabel: "${3}", }, { SourceLabels: model.LabelNames{"a"}, Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"), Action: config.RelabelReplace, Replacement: "${1}", - TargetLabel: model.LabelName("0${3}"), + TargetLabel: "0${3}", }, { SourceLabels: model.LabelNames{"a"}, Regex: config.MustNewRegexp("some-([^-]+)-([^,]+)"), Action: config.RelabelReplace, Replacement: "${1}", - TargetLabel: model.LabelName("-${3}"), + TargetLabel: "-${3}", }, }, output: model.LabelSet{ @@ -353,21 +353,21 @@ func TestRelabel(t *testing.T) { Regex: config.MustNewRegexp("(?:.+,|^)path:(/[^,]+).*"), Action: config.RelabelReplace, Replacement: "${1}", - TargetLabel: model.LabelName("__metrics_path__"), + TargetLabel: "__metrics_path__", }, { SourceLabels: model.LabelNames{"__meta_sd_tags"}, Regex: config.MustNewRegexp("(?:.+,|^)job:([^,]+).*"), Action: config.RelabelReplace, Replacement: "${1}", - TargetLabel: model.LabelName("job"), + TargetLabel: "job", }, { SourceLabels: model.LabelNames{"__meta_sd_tags"}, Regex: config.MustNewRegexp("(?:.+,|^)label:([^=]+)=([^,]+).*"), Action: config.RelabelReplace, Replacement: "${2}", - TargetLabel: model.LabelName("${1}"), + TargetLabel: "${1}", }, }, output: model.LabelSet{ From 7a36af1c852777f265e125459639318f61717196 Mon Sep 17 00:00:00 2001 From: Matti Savolainen Date: Wed, 19 Oct 2016 00:42:49 +0300 Subject: [PATCH 2/6] add comment about interpolation --- config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config/config.go b/config/config.go index 5770f5021..efd0b1f58 100644 --- a/config/config.go +++ b/config/config.go @@ -988,6 +988,7 @@ type RelabelConfig struct { // Modulus to take of the hash of concatenated values from the source labels. Modulus uint64 `yaml:"modulus,omitempty"` // The label to which the resulting string is written in a replacement. + // regex interpolation is allowed for the replace action. TargetLabel string `yaml:"target_label,omitempty"` // Replacement is the regex replacement pattern to be used. Replacement string `yaml:"replacement,omitempty"` From 56907ba6e33646d6db9d2b76fbbf229879d8e4b4 Mon Sep 17 00:00:00 2001 From: Matti Savolainen Date: Wed, 19 Oct 2016 01:19:19 +0300 Subject: [PATCH 3/6] Add interpolation to good test config. Fix regex --- config/config.go | 2 +- config/config_test.go | 11 +++++++++++ config/testdata/conf.good.yml | 7 +++++++ config/testdata/relabel_target_label.good.yml | 0 4 files changed, 19 insertions(+), 1 deletion(-) delete mode 100644 config/testdata/relabel_target_label.good.yml diff --git a/config/config.go b/config/config.go index efd0b1f58..7b63c4061 100644 --- a/config/config.go +++ b/config/config.go @@ -31,7 +31,7 @@ var ( patFileSDName = regexp.MustCompile(`^[^*]*(\*[^/]*)?\.(json|yml|yaml|JSON|YML|YAML)$`) patRulePath = regexp.MustCompile(`^[^*]*(\*[^/]*)?$`) patAuthLine = regexp.MustCompile(`((?:password|bearer_token|secret_key|client_secret):\s+)(".+"|'.+'|[^\s]+)`) - relabelTarget = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$\{?[\w]+}?)+\w*)+$`) + relabelTarget = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$\{?[\w]+\}?)+\w*)+$`) ) // Load parses the YAML input s into a Config. diff --git a/config/config_test.go b/config/config_test.go index 38d885f33..a8cff5ddf 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -207,6 +207,17 @@ var expectedConf = &Config{ Scheme: DefaultConsulSDConfig.Scheme, }, }, + + RelabelConfigs: []*RelabelConfig{ + { + SourceLabels: model.LabelNames{"__meta_sd_consul_tags"}, + Regex: MustNewRegexp("label:([^=]+)=([^,]+)"), + Separator: ",", + TargetLabel: "${1}", + Replacement: "${2}", + Action: RelabelReplace, + }, + }, }, { JobName: "service-z", diff --git a/config/testdata/conf.good.yml b/config/testdata/conf.good.yml index a18deeddb..ab38a56fa 100644 --- a/config/testdata/conf.good.yml +++ b/config/testdata/conf.good.yml @@ -104,6 +104,13 @@ scrape_configs: - server: 'localhost:1234' services: ['nginx', 'cache', 'mysql'] + relabel_configs: + - source_labels: [__meta_sd_consul_tags] + separator: ',' + regex: label:([^=]+)=([^,]+) + target_label: ${1} + replacement: ${2} + - job_name: service-z tls_config: diff --git a/config/testdata/relabel_target_label.good.yml b/config/testdata/relabel_target_label.good.yml deleted file mode 100644 index e69de29bb..000000000 From f867c1fd58c6ef0d8061d71089193de7b9598c9f Mon Sep 17 00:00:00 2001 From: Matti Savolainen Date: Wed, 19 Oct 2016 13:31:55 +0300 Subject: [PATCH 4/6] formating and text fixes, adjust regexp --- config/config.go | 6 +++--- config/testdata/conf.good.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index 7b63c4061..2a9918712 100644 --- a/config/config.go +++ b/config/config.go @@ -31,7 +31,7 @@ var ( patFileSDName = regexp.MustCompile(`^[^*]*(\*[^/]*)?\.(json|yml|yaml|JSON|YML|YAML)$`) patRulePath = regexp.MustCompile(`^[^*]*(\*[^/]*)?$`) patAuthLine = regexp.MustCompile(`((?:password|bearer_token|secret_key|client_secret):\s+)(".+"|'.+'|[^\s]+)`) - relabelTarget = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$\{?[\w]+\}?)+\w*)+$`) + relabelTarget = regexp.MustCompile(`^(?:(?:[a-zA-Z_]|\$(?:\{\w+\}|\w+))+\w*)+$`) ) // Load parses the YAML input s into a Config. @@ -987,8 +987,8 @@ type RelabelConfig struct { Regex Regexp `yaml:"regex,omitempty"` // Modulus to take of the hash of concatenated values from the source labels. Modulus uint64 `yaml:"modulus,omitempty"` - // The label to which the resulting string is written in a replacement. - // regex interpolation is allowed for the replace action. + // TargetLabel is the label to which the resulting string is written in a replacement. + // Regexp interpolation is allowed for the replace action. TargetLabel string `yaml:"target_label,omitempty"` // Replacement is the regex replacement pattern to be used. Replacement string `yaml:"replacement,omitempty"` diff --git a/config/testdata/conf.good.yml b/config/testdata/conf.good.yml index ab38a56fa..a075a3dfe 100644 --- a/config/testdata/conf.good.yml +++ b/config/testdata/conf.good.yml @@ -106,7 +106,7 @@ scrape_configs: relabel_configs: - source_labels: [__meta_sd_consul_tags] - separator: ',' + separator: ',' regex: label:([^=]+)=([^,]+) target_label: ${1} replacement: ${2} From ec6524ce747f2424daae04a7f46ed98461d24bb2 Mon Sep 17 00:00:00 2001 From: Matti Savolainen Date: Wed, 19 Oct 2016 13:32:42 +0300 Subject: [PATCH 5/6] test the labelTarget regex to make sure it properly validates pre-interpolated label names. --- config/config_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/config/config_test.go b/config/config_test.go index a8cff5ddf..4bbb3e9b4 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -512,6 +512,34 @@ func TestEmptyGlobalBlock(t *testing.T) { } } +func TestTargetLabelValidity(t *testing.T) { + tests := []struct { + str string + valid bool + }{ + {"-label", false}, + {"label", true}, + {"label${1}", true}, + {"${1}label", true}, + {"${1}", true}, + {"${1}label", true}, + {"${", false}, + {"$", false}, + {"${}", false}, + {"foo${", false}, + {"$1", true}, + {"asd$2asd", true}, + {"-foo${1}bar-", false}, + {"_${1}_", true}, + {"foo${bar}foo", true}, + } + for _, test := range tests { + if relabelTarget.Match([]byte(test.str)) != test.valid { + t.Fatalf("Expected %q to be %v", test.str, test.valid) + } + } +} + func kubernetesSDHostURL() URL { tURL, _ := url.Parse("https://localhost:1234") return URL{URL: tURL} From aabf4a419bfbc95112b437b7ba0be426c83d8a09 Mon Sep 17 00:00:00 2001 From: Matti Savolainen Date: Wed, 19 Oct 2016 16:30:52 +0300 Subject: [PATCH 6/6] use LabelNam.IsValid() instead of LabelNameRE and MatchString instead of Match --- config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 2a9918712..61775f6e7 100644 --- a/config/config.go +++ b/config/config.go @@ -1015,10 +1015,10 @@ func (c *RelabelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if (c.Action == RelabelReplace || c.Action == RelabelHashMod) && c.TargetLabel == "" { return fmt.Errorf("relabel configuration for %s action requires 'target_label' value", c.Action) } - if c.Action == RelabelReplace && !relabelTarget.Match([]byte(c.TargetLabel)) { + if c.Action == RelabelReplace && !relabelTarget.MatchString(c.TargetLabel) { return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action) } - if c.Action == RelabelHashMod && !model.LabelNameRE.Match([]byte(c.TargetLabel)) { + if c.Action == RelabelHashMod && !model.LabelName(c.TargetLabel).IsValid() { return fmt.Errorf("%q is invalid 'target_label' for %s action", c.TargetLabel, c.Action) } return nil