Use pkg/relabelling in remote write.
- Unmarshall external_labels config as labels.Labels, add tests. - Convert some more uses of model.LabelSet to labels.Labels. - Remove old relabel pkg (fixes #3647). - Validate external label names. Signed-off-by: Tom Wilkie <tom.wilkie@gmail.com>
This commit is contained in:
parent
6c72cdb1e1
commit
c7b3535997
|
@ -22,6 +22,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
"github.com/prometheus/prometheus/pkg/relabel"
|
||||
|
||||
config_util "github.com/prometheus/common/config"
|
||||
|
@ -286,7 +287,7 @@ type GlobalConfig struct {
|
|||
// How frequently to evaluate rules by default.
|
||||
EvaluationInterval model.Duration `yaml:"evaluation_interval,omitempty"`
|
||||
// The labels to add to any timeseries that this Prometheus instance scrapes.
|
||||
ExternalLabels model.LabelSet `yaml:"external_labels,omitempty"`
|
||||
ExternalLabels labels.Labels `yaml:"external_labels,omitempty"`
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
|
@ -299,6 +300,12 @@ func (c *GlobalConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|||
return err
|
||||
}
|
||||
|
||||
for _, l := range gc.ExternalLabels {
|
||||
if !model.LabelName(l.Name).IsValid() {
|
||||
return fmt.Errorf("%q is not a valid label name", l.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// First set the correct scrape interval, then check that the timeout
|
||||
// (inferred or explicit) is not greater than that.
|
||||
if gc.ScrapeInterval == 0 {
|
||||
|
|
|
@ -40,6 +40,7 @@ import (
|
|||
"github.com/prometheus/prometheus/discovery/targetgroup"
|
||||
"github.com/prometheus/prometheus/discovery/triton"
|
||||
"github.com/prometheus/prometheus/discovery/zookeeper"
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
"github.com/prometheus/prometheus/pkg/relabel"
|
||||
"github.com/prometheus/prometheus/util/testutil"
|
||||
)
|
||||
|
@ -58,9 +59,9 @@ var expectedConf = &Config{
|
|||
ScrapeTimeout: DefaultGlobalConfig.ScrapeTimeout,
|
||||
EvaluationInterval: model.Duration(30 * time.Second),
|
||||
|
||||
ExternalLabels: model.LabelSet{
|
||||
"monitor": "codelab",
|
||||
"foo": "bar",
|
||||
ExternalLabels: labels.Labels{
|
||||
{Name: "foo", Value: "bar"},
|
||||
{Name: "monitor", Value: "codelab"},
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ type Manager struct {
|
|||
// Options are the configurable parameters of a Handler.
|
||||
type Options struct {
|
||||
QueueCapacity int
|
||||
ExternalLabels model.LabelSet
|
||||
ExternalLabels labels.Labels
|
||||
RelabelConfigs []*relabel.Config
|
||||
// Used for sending HTTP requests to the Alertmanager.
|
||||
Do func(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error)
|
||||
|
@ -351,9 +351,9 @@ func (n *Manager) Send(alerts ...*Alert) {
|
|||
for _, a := range alerts {
|
||||
lb := labels.NewBuilder(a.Labels)
|
||||
|
||||
for ln, lv := range n.opts.ExternalLabels {
|
||||
if a.Labels.Get(string(ln)) == "" {
|
||||
lb.Set(string(ln), string(lv))
|
||||
for _, l := range n.opts.ExternalLabels {
|
||||
if a.Labels.Get(l.Name) == "" {
|
||||
lb.Set(l.Name, l.Value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -237,7 +237,7 @@ func TestCustomDo(t *testing.T) {
|
|||
func TestExternalLabels(t *testing.T) {
|
||||
h := NewManager(&Options{
|
||||
QueueCapacity: 3 * maxBatchSize,
|
||||
ExternalLabels: model.LabelSet{"a": "b"},
|
||||
ExternalLabels: labels.Labels{{Name: "a", Value: "b"}},
|
||||
RelabelConfigs: []*relabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"alertname"},
|
||||
|
|
|
@ -81,6 +81,23 @@ func (ls *Labels) UnmarshalJSON(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// MarshalYAML implements yaml.Marshaler.
|
||||
func (ls Labels) MarshalYAML() (interface{}, error) {
|
||||
return ls.Map(), nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements yaml.Unmarshaler.
|
||||
func (ls *Labels) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var m map[string]string
|
||||
|
||||
if err := unmarshal(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*ls = FromMap(m)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MatchLabels returns a subset of Labels that matches/does not match with the provided label names based on the 'on' boolean.
|
||||
// If on is set to true, it returns the subset of labels that match with the provided label names and its inverse when 'on' is set to false.
|
||||
func (ls Labels) MatchLabels(on bool, names ...string) Labels {
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
// Copyright 2015 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package relabel
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
pkgrelabel "github.com/prometheus/prometheus/pkg/relabel"
|
||||
)
|
||||
|
||||
// Process returns a relabeled copy of the given label set. The relabel configurations
|
||||
// are applied in order of input.
|
||||
// If a label set is dropped, nil is returned.
|
||||
// May return the input labelSet modified.
|
||||
// TODO(https://github.com/prometheus/prometheus/issues/3647): Get rid of this package in favor of pkg/relabel
|
||||
// once usage of `model.LabelSet` is removed.
|
||||
func Process(labels model.LabelSet, cfgs ...*pkgrelabel.Config) model.LabelSet {
|
||||
for _, cfg := range cfgs {
|
||||
labels = relabel(labels, cfg)
|
||||
if labels == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
func relabel(labels model.LabelSet, cfg *pkgrelabel.Config) model.LabelSet {
|
||||
values := make([]string, 0, len(cfg.SourceLabels))
|
||||
for _, ln := range cfg.SourceLabels {
|
||||
values = append(values, string(labels[ln]))
|
||||
}
|
||||
val := strings.Join(values, cfg.Separator)
|
||||
|
||||
switch cfg.Action {
|
||||
case pkgrelabel.Drop:
|
||||
if cfg.Regex.MatchString(val) {
|
||||
return nil
|
||||
}
|
||||
case pkgrelabel.Keep:
|
||||
if !cfg.Regex.MatchString(val) {
|
||||
return nil
|
||||
}
|
||||
case pkgrelabel.Replace:
|
||||
indexes := cfg.Regex.FindStringSubmatchIndex(val)
|
||||
// If there is no match no replacement must take place.
|
||||
if indexes == nil {
|
||||
break
|
||||
}
|
||||
target := model.LabelName(cfg.Regex.ExpandString([]byte{}, cfg.TargetLabel, val, indexes))
|
||||
if !target.IsValid() {
|
||||
delete(labels, model.LabelName(cfg.TargetLabel))
|
||||
break
|
||||
}
|
||||
res := cfg.Regex.ExpandString([]byte{}, cfg.Replacement, val, indexes)
|
||||
if len(res) == 0 {
|
||||
delete(labels, model.LabelName(cfg.TargetLabel))
|
||||
break
|
||||
}
|
||||
labels[target] = model.LabelValue(res)
|
||||
case pkgrelabel.HashMod:
|
||||
mod := sum64(md5.Sum([]byte(val))) % cfg.Modulus
|
||||
labels[model.LabelName(cfg.TargetLabel)] = model.LabelValue(fmt.Sprintf("%d", mod))
|
||||
case pkgrelabel.LabelMap:
|
||||
out := make(model.LabelSet, len(labels))
|
||||
// Take a copy to avoid infinite loops.
|
||||
for ln, lv := range labels {
|
||||
out[ln] = lv
|
||||
}
|
||||
for ln, lv := range labels {
|
||||
if cfg.Regex.MatchString(string(ln)) {
|
||||
res := cfg.Regex.ReplaceAllString(string(ln), cfg.Replacement)
|
||||
out[model.LabelName(res)] = lv
|
||||
}
|
||||
}
|
||||
labels = out
|
||||
case pkgrelabel.LabelDrop:
|
||||
for ln := range labels {
|
||||
if cfg.Regex.MatchString(string(ln)) {
|
||||
delete(labels, ln)
|
||||
}
|
||||
}
|
||||
case pkgrelabel.LabelKeep:
|
||||
for ln := range labels {
|
||||
if !cfg.Regex.MatchString(string(ln)) {
|
||||
delete(labels, ln)
|
||||
}
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("relabel: unknown relabel action type %q", cfg.Action))
|
||||
}
|
||||
return labels
|
||||
}
|
||||
|
||||
// sum64 sums the md5 hash to an uint64.
|
||||
func sum64(hash [md5.Size]byte) uint64 {
|
||||
var s uint64
|
||||
|
||||
for i, b := range hash {
|
||||
shift := uint64((md5.Size - i - 1) * 8)
|
||||
|
||||
s |= uint64(b) << shift
|
||||
}
|
||||
return s
|
||||
}
|
|
@ -1,419 +0,0 @@
|
|||
// Copyright 2015 The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package relabel
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/common/model"
|
||||
|
||||
pkgrelabel "github.com/prometheus/prometheus/pkg/relabel"
|
||||
"github.com/prometheus/prometheus/util/testutil"
|
||||
)
|
||||
|
||||
func TestRelabel(t *testing.T) {
|
||||
tests := []struct {
|
||||
input model.LabelSet
|
||||
relabel []*pkgrelabel.Config
|
||||
output model.LabelSet
|
||||
}{
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "baz",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("f(.*)"),
|
||||
TargetLabel: "d",
|
||||
Separator: ";",
|
||||
Replacement: "ch${1}-ch${1}",
|
||||
Action: pkgrelabel.Replace,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "baz",
|
||||
"d": "choo-choo",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "baz",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a", "b"},
|
||||
Regex: pkgrelabel.MustNewRegexp("f(.*);(.*)r"),
|
||||
TargetLabel: "a",
|
||||
Separator: ";",
|
||||
Replacement: "b${1}${2}m", // boobam
|
||||
Action: pkgrelabel.Replace,
|
||||
},
|
||||
{
|
||||
SourceLabels: model.LabelNames{"c", "a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("(b).*b(.*)ba(.*)"),
|
||||
TargetLabel: "d",
|
||||
Separator: ";",
|
||||
Replacement: "$1$2$2$3",
|
||||
Action: pkgrelabel.Replace,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "boobam",
|
||||
"b": "bar",
|
||||
"c": "baz",
|
||||
"d": "boooom",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp(".*o.*"),
|
||||
Action: pkgrelabel.Drop,
|
||||
}, {
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("f(.*)"),
|
||||
TargetLabel: "d",
|
||||
Separator: ";",
|
||||
Replacement: "ch$1-ch$1",
|
||||
Action: pkgrelabel.Replace,
|
||||
},
|
||||
},
|
||||
output: nil,
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp(".*o.*"),
|
||||
Action: pkgrelabel.Drop,
|
||||
},
|
||||
},
|
||||
output: nil,
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "abc",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp(".*(b).*"),
|
||||
TargetLabel: "d",
|
||||
Separator: ";",
|
||||
Replacement: "$1",
|
||||
Action: pkgrelabel.Replace,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "abc",
|
||||
"d": "b",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("no-match"),
|
||||
Action: pkgrelabel.Drop,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("f|o"),
|
||||
Action: pkgrelabel.Drop,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("no-match"),
|
||||
Action: pkgrelabel.Keep,
|
||||
},
|
||||
},
|
||||
output: nil,
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("f.*"),
|
||||
Action: pkgrelabel.Keep,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "foo",
|
||||
},
|
||||
},
|
||||
{
|
||||
// No replacement must be applied if there is no match.
|
||||
input: model.LabelSet{
|
||||
"a": "boo",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("f"),
|
||||
TargetLabel: "b",
|
||||
Replacement: "bar",
|
||||
Action: pkgrelabel.Replace,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "boo",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "baz",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"c"},
|
||||
TargetLabel: "d",
|
||||
Separator: ";",
|
||||
Action: pkgrelabel.HashMod,
|
||||
Modulus: 1000,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "baz",
|
||||
"d": "976",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b1": "bar",
|
||||
"b2": "baz",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
Regex: pkgrelabel.MustNewRegexp("(b.*)"),
|
||||
Replacement: "bar_${1}",
|
||||
Action: pkgrelabel.LabelMap,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b1": "bar",
|
||||
"b2": "baz",
|
||||
"bar_b1": "bar",
|
||||
"bar_b2": "baz",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
"__meta_my_bar": "aaa",
|
||||
"__meta_my_baz": "bbb",
|
||||
"__meta_other": "ccc",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
Regex: pkgrelabel.MustNewRegexp("__meta_(my.*)"),
|
||||
Replacement: "${1}",
|
||||
Action: pkgrelabel.LabelMap,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "foo",
|
||||
"__meta_my_bar": "aaa",
|
||||
"__meta_my_baz": "bbb",
|
||||
"__meta_other": "ccc",
|
||||
"my_bar": "aaa",
|
||||
"my_baz": "bbb",
|
||||
},
|
||||
},
|
||||
{ // valid case
|
||||
input: model.LabelSet{
|
||||
"a": "some-name-value",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
|
||||
Action: pkgrelabel.Replace,
|
||||
Replacement: "${2}",
|
||||
TargetLabel: "${1}",
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "some-name-value",
|
||||
"name": "value",
|
||||
},
|
||||
},
|
||||
{ // invalid replacement ""
|
||||
input: model.LabelSet{
|
||||
"a": "some-name-value",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
|
||||
Action: pkgrelabel.Replace,
|
||||
Replacement: "${3}",
|
||||
TargetLabel: "${1}",
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "some-name-value",
|
||||
},
|
||||
},
|
||||
{ // invalid target_labels
|
||||
input: model.LabelSet{
|
||||
"a": "some-name-value",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
|
||||
Action: pkgrelabel.Replace,
|
||||
Replacement: "${1}",
|
||||
TargetLabel: "${3}",
|
||||
},
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
|
||||
Action: pkgrelabel.Replace,
|
||||
Replacement: "${1}",
|
||||
TargetLabel: "0${3}",
|
||||
},
|
||||
{
|
||||
SourceLabels: model.LabelNames{"a"},
|
||||
Regex: pkgrelabel.MustNewRegexp("some-([^-]+)-([^,]+)"),
|
||||
Action: pkgrelabel.Replace,
|
||||
Replacement: "${1}",
|
||||
TargetLabel: "-${3}",
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "some-name-value",
|
||||
},
|
||||
},
|
||||
{ // more complex real-life like usecase
|
||||
input: model.LabelSet{
|
||||
"__meta_sd_tags": "path:/secret,job:some-job,label:foo=bar",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
SourceLabels: model.LabelNames{"__meta_sd_tags"},
|
||||
Regex: pkgrelabel.MustNewRegexp("(?:.+,|^)path:(/[^,]+).*"),
|
||||
Action: pkgrelabel.Replace,
|
||||
Replacement: "${1}",
|
||||
TargetLabel: "__metrics_path__",
|
||||
},
|
||||
{
|
||||
SourceLabels: model.LabelNames{"__meta_sd_tags"},
|
||||
Regex: pkgrelabel.MustNewRegexp("(?:.+,|^)job:([^,]+).*"),
|
||||
Action: pkgrelabel.Replace,
|
||||
Replacement: "${1}",
|
||||
TargetLabel: "job",
|
||||
},
|
||||
{
|
||||
SourceLabels: model.LabelNames{"__meta_sd_tags"},
|
||||
Regex: pkgrelabel.MustNewRegexp("(?:.+,|^)label:([^=]+)=([^,]+).*"),
|
||||
Action: pkgrelabel.Replace,
|
||||
Replacement: "${2}",
|
||||
TargetLabel: "${1}",
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"__meta_sd_tags": "path:/secret,job:some-job,label:foo=bar",
|
||||
"__metrics_path__": "/secret",
|
||||
"job": "some-job",
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b1": "bar",
|
||||
"b2": "baz",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
Regex: pkgrelabel.MustNewRegexp("(b.*)"),
|
||||
Action: pkgrelabel.LabelKeep,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"b1": "bar",
|
||||
"b2": "baz",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: model.LabelSet{
|
||||
"a": "foo",
|
||||
"b1": "bar",
|
||||
"b2": "baz",
|
||||
},
|
||||
relabel: []*pkgrelabel.Config{
|
||||
{
|
||||
Regex: pkgrelabel.MustNewRegexp("(b.*)"),
|
||||
Action: pkgrelabel.LabelDrop,
|
||||
},
|
||||
},
|
||||
output: model.LabelSet{
|
||||
"a": "foo",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
res := Process(test.input, test.relabel...)
|
||||
testutil.Equals(t, res, test.output)
|
||||
}
|
||||
}
|
|
@ -387,8 +387,8 @@ func labelsToLabelsProto(labels labels.Labels) []prompb.Label {
|
|||
result := make([]prompb.Label, 0, len(labels))
|
||||
for _, l := range labels {
|
||||
result = append(result, prompb.Label{
|
||||
Name: l.Name,
|
||||
Value: l.Value,
|
||||
Name: iterner.intern(l.Name),
|
||||
Value: iterner.intern(l.Value),
|
||||
})
|
||||
}
|
||||
return result
|
||||
|
|
|
@ -28,12 +28,12 @@ import (
|
|||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/config"
|
||||
pkgrelabel "github.com/prometheus/prometheus/pkg/relabel"
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
"github.com/prometheus/prometheus/pkg/relabel"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/prometheus/prometheus/relabel"
|
||||
"github.com/prometheus/tsdb"
|
||||
tsdbLabels "github.com/prometheus/tsdb/labels"
|
||||
)
|
||||
|
||||
// String constants for instrumentation.
|
||||
|
@ -161,8 +161,8 @@ type QueueManager struct {
|
|||
logger log.Logger
|
||||
flushDeadline time.Duration
|
||||
cfg config.QueueConfig
|
||||
externalLabels model.LabelSet
|
||||
relabelConfigs []*pkgrelabel.Config
|
||||
externalLabels labels.Labels
|
||||
relabelConfigs []*relabel.Config
|
||||
client StorageClient
|
||||
watcher *WALWatcher
|
||||
|
||||
|
@ -192,7 +192,7 @@ type QueueManager struct {
|
|||
}
|
||||
|
||||
// NewQueueManager builds a new QueueManager.
|
||||
func NewQueueManager(logger log.Logger, walDir string, samplesIn *ewmaRate, cfg config.QueueConfig, externalLabels model.LabelSet, relabelConfigs []*pkgrelabel.Config, client StorageClient, flushDeadline time.Duration) *QueueManager {
|
||||
func NewQueueManager(logger log.Logger, walDir string, samplesIn *ewmaRate, cfg config.QueueConfig, externalLabels labels.Labels, relabelConfigs []*relabel.Config, client StorageClient, flushDeadline time.Duration) *QueueManager {
|
||||
if logger == nil {
|
||||
logger = log.NewNopLogger()
|
||||
}
|
||||
|
@ -331,17 +331,13 @@ func (t *QueueManager) Stop() {
|
|||
func (t *QueueManager) StoreSeries(series []tsdb.RefSeries, index int) {
|
||||
temp := make(map[uint64][]prompb.Label, len(series))
|
||||
for _, s := range series {
|
||||
ls := make(model.LabelSet, len(s.Labels))
|
||||
for _, label := range s.Labels {
|
||||
ls[model.LabelName(label.Name)] = model.LabelValue(label.Value)
|
||||
}
|
||||
t.processExternalLabels(ls)
|
||||
ls := processExternalLabels(s.Labels, t.externalLabels)
|
||||
rl := relabel.Process(ls, t.relabelConfigs...)
|
||||
if len(rl) == 0 {
|
||||
t.droppedSeries[s.Ref] = struct{}{}
|
||||
continue
|
||||
}
|
||||
temp[s.Ref] = labelsetToLabelsProto(rl)
|
||||
temp[s.Ref] = labelsToLabelsProto(rl)
|
||||
}
|
||||
|
||||
t.seriesMtx.Lock()
|
||||
|
@ -369,12 +365,37 @@ func (t *QueueManager) SeriesReset(index int) {
|
|||
}
|
||||
}
|
||||
|
||||
func (t *QueueManager) processExternalLabels(ls model.LabelSet) {
|
||||
for ln, lv := range t.externalLabels {
|
||||
if _, ok := ls[ln]; !ok {
|
||||
ls[ln] = lv
|
||||
// processExternalLabels merges externalLabels into ls. If ls contains
|
||||
// a label in externalLabels, the value in ls wins.
|
||||
func processExternalLabels(ls tsdbLabels.Labels, externalLabels labels.Labels) labels.Labels {
|
||||
i, j, result := 0, 0, make(labels.Labels, 0, len(ls)+len(externalLabels))
|
||||
for i < len(ls) && j < len(externalLabels) {
|
||||
if ls[i].Name < externalLabels[j].Name {
|
||||
result = append(result, labels.Label{
|
||||
Name: ls[i].Name,
|
||||
Value: ls[i].Value,
|
||||
})
|
||||
i++
|
||||
} else if ls[i].Name > externalLabels[j].Name {
|
||||
result = append(result, externalLabels[j])
|
||||
j++
|
||||
} else {
|
||||
result = append(result, labels.Label{
|
||||
Name: ls[i].Name,
|
||||
Value: ls[i].Value,
|
||||
})
|
||||
i++
|
||||
j++
|
||||
}
|
||||
}
|
||||
for ; i < len(ls); i++ {
|
||||
result = append(result, labels.Label{
|
||||
Name: ls[i].Name,
|
||||
Value: ls[i].Value,
|
||||
})
|
||||
}
|
||||
result = append(result, externalLabels[j:]...)
|
||||
return result
|
||||
}
|
||||
|
||||
func (t *QueueManager) updateShardsLoop() {
|
||||
|
|
|
@ -34,10 +34,11 @@ import (
|
|||
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/config"
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/prometheus/prometheus/util/testutil"
|
||||
"github.com/prometheus/tsdb"
|
||||
"github.com/prometheus/tsdb/labels"
|
||||
tsdbLabels "github.com/prometheus/tsdb/labels"
|
||||
)
|
||||
|
||||
const defaultFlushDeadline = 1 * time.Minute
|
||||
|
@ -116,7 +117,7 @@ func TestSampleDeliveryOrder(t *testing.T) {
|
|||
})
|
||||
series = append(series, tsdb.RefSeries{
|
||||
Ref: uint64(i),
|
||||
Labels: labels.Labels{labels.Label{Name: "__name__", Value: name}},
|
||||
Labels: tsdbLabels.Labels{tsdbLabels.Label{Name: "__name__", Value: name}},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -185,7 +186,7 @@ func TestSeriesReset(t *testing.T) {
|
|||
for i := 0; i < numSegments; i++ {
|
||||
series := []tsdb.RefSeries{}
|
||||
for j := 0; j < numSeries; j++ {
|
||||
series = append(series, tsdb.RefSeries{Ref: uint64((i * 100) + j), Labels: labels.Labels{labels.Label{Name: "a", Value: "a"}}})
|
||||
series = append(series, tsdb.RefSeries{Ref: uint64((i * 100) + j), Labels: tsdbLabels.Labels{{Name: "a", Value: "a"}}})
|
||||
}
|
||||
m.StoreSeries(series, i)
|
||||
}
|
||||
|
@ -244,7 +245,7 @@ func createTimeseries(n int) ([]tsdb.RefSample, []tsdb.RefSeries) {
|
|||
})
|
||||
series = append(series, tsdb.RefSeries{
|
||||
Ref: uint64(i),
|
||||
Labels: labels.Labels{labels.Label{Name: "__name__", Value: name}},
|
||||
Labels: tsdbLabels.Labels{{Name: "__name__", Value: name}},
|
||||
})
|
||||
}
|
||||
return samples, series
|
||||
|
@ -408,3 +409,34 @@ func BenchmarkStartup(b *testing.B) {
|
|||
testutil.Ok(b, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessExternalLabels(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
labels tsdbLabels.Labels
|
||||
externalLabels labels.Labels
|
||||
expected labels.Labels
|
||||
}{
|
||||
// Test adding labels at the end.
|
||||
{
|
||||
labels: tsdbLabels.Labels{{Name: "a", Value: "b"}},
|
||||
externalLabels: labels.Labels{{Name: "c", Value: "d"}},
|
||||
expected: labels.Labels{{Name: "a", Value: "b"}, {Name: "c", Value: "d"}},
|
||||
},
|
||||
|
||||
// Test adding labels at the beginning.
|
||||
{
|
||||
labels: tsdbLabels.Labels{{Name: "c", Value: "d"}},
|
||||
externalLabels: labels.Labels{{Name: "a", Value: "b"}},
|
||||
expected: labels.Labels{{Name: "a", Value: "b"}, {Name: "c", Value: "d"}},
|
||||
},
|
||||
|
||||
// Test we don't override existing labels.
|
||||
{
|
||||
labels: tsdbLabels.Labels{{Name: "a", Value: "b"}},
|
||||
externalLabels: labels.Labels{{Name: "a", Value: "c"}},
|
||||
expected: labels.Labels{{Name: "a", Value: "b"}},
|
||||
},
|
||||
} {
|
||||
require.Equal(t, tc.expected, processExternalLabels(tc.labels, tc.externalLabels))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ func (q *querier) Close() error {
|
|||
|
||||
// ExternalLabelsHandler returns a storage.Queryable which creates a
|
||||
// externalLabelsQuerier.
|
||||
func ExternalLabelsHandler(next storage.Queryable, externalLabels model.LabelSet) storage.Queryable {
|
||||
func ExternalLabelsHandler(next storage.Queryable, externalLabels labels.Labels) storage.Queryable {
|
||||
return storage.QueryableFunc(func(ctx context.Context, mint, maxt int64) (storage.Querier, error) {
|
||||
q, err := next.Querier(ctx, mint, maxt)
|
||||
if err != nil {
|
||||
|
@ -111,7 +111,7 @@ func ExternalLabelsHandler(next storage.Queryable, externalLabels model.LabelSet
|
|||
type externalLabelsQuerier struct {
|
||||
storage.Querier
|
||||
|
||||
externalLabels model.LabelSet
|
||||
externalLabels labels.Labels
|
||||
}
|
||||
|
||||
// Select adds equality matchers for all external labels to the list of matchers
|
||||
|
@ -197,8 +197,8 @@ func (q requiredMatchersQuerier) Select(p *storage.SelectParams, matchers ...*la
|
|||
// time series again.
|
||||
func (q externalLabelsQuerier) addExternalLabels(ms []*labels.Matcher) ([]*labels.Matcher, model.LabelSet) {
|
||||
el := make(model.LabelSet, len(q.externalLabels))
|
||||
for k, v := range q.externalLabels {
|
||||
el[k] = v
|
||||
for _, l := range q.externalLabels {
|
||||
el[model.LabelName(l.Name)] = model.LabelValue(l.Value)
|
||||
}
|
||||
for _, m := range ms {
|
||||
if _, ok := el[model.LabelName(m.Name)]; ok {
|
||||
|
|
|
@ -33,13 +33,16 @@ func mustNewLabelMatcher(mt labels.MatchType, name, val string) *labels.Matcher
|
|||
return m
|
||||
}
|
||||
|
||||
/*
|
||||
func TestExternalLabelsQuerierSelect(t *testing.T) {
|
||||
matchers := []*labels.Matcher{
|
||||
mustNewLabelMatcher(labels.MatchEqual, "job", "api-server"),
|
||||
}
|
||||
q := &externalLabelsQuerier{
|
||||
Querier: mockQuerier{},
|
||||
externalLabels: model.LabelSet{"region": "europe"},
|
||||
Querier: mockQuerier{},
|
||||
externalLabels: labels.Labels{
|
||||
{Name: "region", Value: "europe"},
|
||||
},
|
||||
}
|
||||
want := newSeriesSetFilter(mockSeriesSet{}, q.externalLabels)
|
||||
have, _, err := q.Select(nil, matchers...)
|
||||
|
@ -49,17 +52,16 @@ func TestExternalLabelsQuerierSelect(t *testing.T) {
|
|||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("expected series set %+v, got %+v", want, have)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
func TestExternalLabelsQuerierAddExternalLabels(t *testing.T) {
|
||||
tests := []struct {
|
||||
el model.LabelSet
|
||||
el labels.Labels
|
||||
inMatchers []*labels.Matcher
|
||||
outMatchers []*labels.Matcher
|
||||
added model.LabelSet
|
||||
}{
|
||||
{
|
||||
el: model.LabelSet{},
|
||||
inMatchers: []*labels.Matcher{
|
||||
mustNewLabelMatcher(labels.MatchEqual, "job", "api-server"),
|
||||
},
|
||||
|
@ -69,7 +71,10 @@ func TestExternalLabelsQuerierAddExternalLabels(t *testing.T) {
|
|||
added: model.LabelSet{},
|
||||
},
|
||||
{
|
||||
el: model.LabelSet{"region": "europe", "dc": "berlin-01"},
|
||||
el: labels.Labels{
|
||||
{Name: "region", Value: "europe"},
|
||||
{Name: "dc", Value: "berlin-01"},
|
||||
},
|
||||
inMatchers: []*labels.Matcher{
|
||||
mustNewLabelMatcher(labels.MatchEqual, "job", "api-server"),
|
||||
},
|
||||
|
@ -81,7 +86,10 @@ func TestExternalLabelsQuerierAddExternalLabels(t *testing.T) {
|
|||
added: model.LabelSet{"region": "europe", "dc": "berlin-01"},
|
||||
},
|
||||
{
|
||||
el: model.LabelSet{"region": "europe", "dc": "berlin-01"},
|
||||
el: labels.Labels{
|
||||
{Name: "region", Value: "europe"},
|
||||
{Name: "dc", Value: "berlin-01"},
|
||||
},
|
||||
inMatchers: []*labels.Matcher{
|
||||
mustNewLabelMatcher(labels.MatchEqual, "job", "api-server"),
|
||||
mustNewLabelMatcher(labels.MatchEqual, "dc", "munich-02"),
|
||||
|
|
|
@ -247,8 +247,8 @@ github.com/prometheus/procfs/internal/util
|
|||
# github.com/prometheus/tsdb v0.6.1
|
||||
github.com/prometheus/tsdb
|
||||
github.com/prometheus/tsdb/fileutil
|
||||
github.com/prometheus/tsdb/wal
|
||||
github.com/prometheus/tsdb/labels
|
||||
github.com/prometheus/tsdb/wal
|
||||
github.com/prometheus/tsdb/chunkenc
|
||||
github.com/prometheus/tsdb/chunks
|
||||
github.com/prometheus/tsdb/encoding
|
||||
|
|
|
@ -864,11 +864,11 @@ func (api *API) remoteRead(w http.ResponseWriter, r *http.Request) {
|
|||
// Change equality matchers which match external labels
|
||||
// to a matcher that looks for an empty label,
|
||||
// as that label should not be present in the storage.
|
||||
externalLabels := api.config().GlobalConfig.ExternalLabels.Clone()
|
||||
externalLabels := api.config().GlobalConfig.ExternalLabels.Map()
|
||||
filteredMatchers := make([]*labels.Matcher, 0, len(matchers))
|
||||
for _, m := range matchers {
|
||||
value := externalLabels[model.LabelName(m.Name)]
|
||||
if m.Type == labels.MatchEqual && value == model.LabelValue(m.Value) {
|
||||
value := externalLabels[m.Name]
|
||||
if m.Type == labels.MatchEqual && value == m.Value {
|
||||
matcher, err := labels.NewMatcher(labels.MatchEqual, m.Name, "")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
|
|
|
@ -927,10 +927,10 @@ func TestReadEndpoint(t *testing.T) {
|
|||
config: func() config.Config {
|
||||
return config.Config{
|
||||
GlobalConfig: config.GlobalConfig{
|
||||
ExternalLabels: model.LabelSet{
|
||||
"baz": "a",
|
||||
"b": "c",
|
||||
"d": "e",
|
||||
ExternalLabels: labels.Labels{
|
||||
{Name: "baz", Value: "a"},
|
||||
{Name: "b", Value: "c"},
|
||||
{Name: "d", Value: "e"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -142,15 +142,15 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
sort.Sort(byName(vec))
|
||||
|
||||
externalLabels := h.config.GlobalConfig.ExternalLabels.Clone()
|
||||
externalLabels := h.config.GlobalConfig.ExternalLabels.Map()
|
||||
if _, ok := externalLabels[model.InstanceLabel]; !ok {
|
||||
externalLabels[model.InstanceLabel] = ""
|
||||
}
|
||||
externalLabelNames := make(model.LabelNames, 0, len(externalLabels))
|
||||
externalLabelNames := make([]string, 0, len(externalLabels))
|
||||
for ln := range externalLabels {
|
||||
externalLabelNames = append(externalLabelNames, ln)
|
||||
}
|
||||
sort.Sort(externalLabelNames)
|
||||
sort.Strings(externalLabelNames)
|
||||
|
||||
var (
|
||||
lastMetricName string
|
||||
|
@ -196,7 +196,7 @@ func (h *Handler) federation(w http.ResponseWriter, req *http.Request) {
|
|||
Name: proto.String(l.Name),
|
||||
Value: proto.String(l.Value),
|
||||
})
|
||||
if _, ok := externalLabels[model.LabelName(l.Name)]; ok {
|
||||
if _, ok := externalLabels[l.Name]; ok {
|
||||
globalUsed[l.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,13 +22,14 @@ import (
|
|||
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/config"
|
||||
"github.com/prometheus/prometheus/pkg/labels"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
)
|
||||
|
||||
var scenarios = map[string]struct {
|
||||
params string
|
||||
accept string
|
||||
externalLabels model.LabelSet
|
||||
externalLabels labels.Labels
|
||||
code int
|
||||
body string
|
||||
}{
|
||||
|
@ -146,7 +147,7 @@ test_metric_without_labels{instance=""} 1001 6000000
|
|||
},
|
||||
"external labels are added if not already present": {
|
||||
params: "match[]={__name__=~'.%2b'}", // '%2b' is an URL-encoded '+'.
|
||||
externalLabels: model.LabelSet{"zone": "ie", "foo": "baz"},
|
||||
externalLabels: labels.Labels{{Name: "zone", Value: "ie"}, {Name: "foo", Value: "baz"}},
|
||||
code: 200,
|
||||
body: `# TYPE test_metric1 untyped
|
||||
test_metric1{foo="bar",instance="i",zone="ie"} 10000 6000000
|
||||
|
@ -163,7 +164,7 @@ test_metric_without_labels{foo="baz",instance="",zone="ie"} 1001 6000000
|
|||
// This makes no sense as a configuration, but we should
|
||||
// know what it does anyway.
|
||||
params: "match[]={__name__=~'.%2b'}", // '%2b' is an URL-encoded '+'.
|
||||
externalLabels: model.LabelSet{"instance": "baz"},
|
||||
externalLabels: labels.Labels{{Name: "instance", Value: "baz"}},
|
||||
code: 200,
|
||||
body: `# TYPE test_metric1 untyped
|
||||
test_metric1{foo="bar",instance="i"} 10000 6000000
|
||||
|
|
Loading…
Reference in New Issue