mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-30 15:22:07 +00:00
lib/promrelabel: add support for conditional relabeling via if
filter
Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1998
This commit is contained in:
parent
6fd85117ac
commit
2431c9cf81
8 changed files with 878 additions and 7 deletions
|
@ -264,7 +264,7 @@ Labels can be added to metrics by the following mechanisms:
|
||||||
|
|
||||||
## Relabeling
|
## Relabeling
|
||||||
|
|
||||||
`vmagent` and VictoriaMetrics support Prometheus-compatible relabeling.
|
VictoriaMetrics components (including `vmagent`) support Prometheus-compatible relabeling.
|
||||||
They provide the following additional actions on top of actions from the [Prometheus relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config):
|
They provide the following additional actions on top of actions from the [Prometheus relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config):
|
||||||
|
|
||||||
* `replace_all`: replaces all of the occurences of `regex` in the values of `source_labels` with the `replacement` and stores the results in the `target_label`.
|
* `replace_all`: replaces all of the occurences of `regex` in the values of `source_labels` with the `replacement` and stores the results in the `target_label`.
|
||||||
|
@ -289,6 +289,21 @@ The `regex` value can be split into multiple lines for improved readability and
|
||||||
- "foo_.+"
|
- "foo_.+"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
VictoriaMetrics components support an optional `if` filter, which can be used for conditional relabeling. The `if` filter may contain arbitrary [time series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors). For example, the following relabeling rule drops targets, which don't match `foo{bar="baz"}` series selector:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- action: keep
|
||||||
|
if: 'foo{bar="baz"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
This is equivalent to less clear traditional relabeling rule:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- action: keep
|
||||||
|
source_labels: [__name__, bar]
|
||||||
|
regex: 'foo;baz'
|
||||||
|
```
|
||||||
|
|
||||||
The relabeling can be defined in the following places:
|
The relabeling can be defined in the following places:
|
||||||
|
|
||||||
* At the `scrape_config -> relabel_configs` section in `-promscrape.config` file. This relabeling is applied to target labels. This relabeling can be debugged by passing `relabel_debug: true` option to the corresponding `scrape_config` section. In this case `vmagent` logs target labels before and after the relabeling and then drops the logged target.
|
* At the `scrape_config -> relabel_configs` section in `-promscrape.config` file. This relabeling is applied to target labels. This relabeling can be debugged by passing `relabel_debug: true` option to the corresponding `scrape_config` section. In this case `vmagent` logs target labels before and after the relabeling and then drops the logged target.
|
||||||
|
|
|
@ -14,6 +14,23 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||||
|
|
||||||
## tip
|
## tip
|
||||||
|
|
||||||
|
* FEATURE: add support for conditional relabeling via `if` filter. The `if` filter can contain arbitrary [series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors). For example, the following rule drops targets matching `foo{bar="baz"}` series selector:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- action: drop
|
||||||
|
if: 'foo{bar="baz"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
This rule is equivalent to less clear traditional one:
|
||||||
|
|
||||||
|
```yml
|
||||||
|
- action: drop
|
||||||
|
source_labels: [__name__, bar]
|
||||||
|
regex: 'foo;baz'
|
||||||
|
```
|
||||||
|
|
||||||
|
See [relabeling docs](https://docs.victoriametrics.com/vmagent.html#relabeling) and [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1998) for more details.
|
||||||
|
|
||||||
* FEATURE: reduce memory usage for various caches under [high churn rate](https://docs.victoriametrics.com/FAQ.html#what-is-high-churn-rate).
|
* FEATURE: reduce memory usage for various caches under [high churn rate](https://docs.victoriametrics.com/FAQ.html#what-is-high-churn-rate).
|
||||||
|
|
||||||
* BUGFIX: [vmgateway](https://docs.victoriametrics.com/vmgateway.html): properly parse JWT tokens if they are encoded with [URL-safe base64 encoding](https://datatracker.ietf.org/doc/html/rfc4648#section-5).
|
* BUGFIX: [vmgateway](https://docs.victoriametrics.com/vmgateway.html): properly parse JWT tokens if they are encoded with [URL-safe base64 encoding](https://datatracker.ietf.org/doc/html/rfc4648#section-5).
|
||||||
|
|
|
@ -268,7 +268,7 @@ Labels can be added to metrics by the following mechanisms:
|
||||||
|
|
||||||
## Relabeling
|
## Relabeling
|
||||||
|
|
||||||
`vmagent` and VictoriaMetrics support Prometheus-compatible relabeling.
|
VictoriaMetrics components (including `vmagent`) support Prometheus-compatible relabeling.
|
||||||
They provide the following additional actions on top of actions from the [Prometheus relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config):
|
They provide the following additional actions on top of actions from the [Prometheus relabeling](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config):
|
||||||
|
|
||||||
* `replace_all`: replaces all of the occurences of `regex` in the values of `source_labels` with the `replacement` and stores the results in the `target_label`.
|
* `replace_all`: replaces all of the occurences of `regex` in the values of `source_labels` with the `replacement` and stores the results in the `target_label`.
|
||||||
|
@ -293,6 +293,21 @@ The `regex` value can be split into multiple lines for improved readability and
|
||||||
- "foo_.+"
|
- "foo_.+"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
VictoriaMetrics components support an optional `if` filter, which can be used for conditional relabeling. The `if` filter may contain arbitrary [time series selector](https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors). For example, the following relabeling rule drops targets, which don't match `foo{bar="baz"}` series selector:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- action: keep
|
||||||
|
if: 'foo{bar="baz"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
This is equivalent to less clear traditional relabeling rule:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- action: keep
|
||||||
|
source_labels: [__name__, bar]
|
||||||
|
regex: 'foo;baz'
|
||||||
|
```
|
||||||
|
|
||||||
The relabeling can be defined in the following places:
|
The relabeling can be defined in the following places:
|
||||||
|
|
||||||
* At the `scrape_config -> relabel_configs` section in `-promscrape.config` file. This relabeling is applied to target labels. This relabeling can be debugged by passing `relabel_debug: true` option to the corresponding `scrape_config` section. In this case `vmagent` logs target labels before and after the relabeling and then drops the logged target.
|
* At the `scrape_config -> relabel_configs` section in `-promscrape.config` file. This relabeling is applied to target labels. This relabeling can be debugged by passing `relabel_debug: true` option to the corresponding `scrape_config` section. In this case `vmagent` logs target labels before and after the relabeling and then drops the logged target.
|
||||||
|
|
|
@ -22,6 +22,7 @@ type RelabelConfig struct {
|
||||||
Modulus uint64 `yaml:"modulus,omitempty"`
|
Modulus uint64 `yaml:"modulus,omitempty"`
|
||||||
Replacement *string `yaml:"replacement,omitempty"`
|
Replacement *string `yaml:"replacement,omitempty"`
|
||||||
Action string `yaml:"action,omitempty"`
|
Action string `yaml:"action,omitempty"`
|
||||||
|
If *IfExpression `yaml:"if,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiLineRegex contains a regex, which can be split into multiple lines.
|
// MultiLineRegex contains a regex, which can be split into multiple lines.
|
||||||
|
@ -44,7 +45,7 @@ type MultiLineRegex struct {
|
||||||
func (mlr *MultiLineRegex) UnmarshalYAML(f func(interface{}) error) error {
|
func (mlr *MultiLineRegex) UnmarshalYAML(f func(interface{}) error) error {
|
||||||
var v interface{}
|
var v interface{}
|
||||||
if err := f(&v); err != nil {
|
if err := f(&v); err != nil {
|
||||||
return err
|
return fmt.Errorf("cannot parse multiline regex: %w", err)
|
||||||
}
|
}
|
||||||
s, err := stringValue(v)
|
s, err := stringValue(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -224,11 +225,11 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
|
||||||
return nil, fmt.Errorf("`source_labels` must contain at least two entries for `action=drop_if_equal`; got %q", sourceLabels)
|
return nil, fmt.Errorf("`source_labels` must contain at least two entries for `action=drop_if_equal`; got %q", sourceLabels)
|
||||||
}
|
}
|
||||||
case "keep":
|
case "keep":
|
||||||
if len(sourceLabels) == 0 {
|
if len(sourceLabels) == 0 && rc.If == nil {
|
||||||
return nil, fmt.Errorf("missing `source_labels` for `action=keep`")
|
return nil, fmt.Errorf("missing `source_labels` for `action=keep`")
|
||||||
}
|
}
|
||||||
case "drop":
|
case "drop":
|
||||||
if len(sourceLabels) == 0 {
|
if len(sourceLabels) == 0 && rc.If == nil {
|
||||||
return nil, fmt.Errorf("missing `source_labels` for `action=drop`")
|
return nil, fmt.Errorf("missing `source_labels` for `action=drop`")
|
||||||
}
|
}
|
||||||
case "hashmod":
|
case "hashmod":
|
||||||
|
@ -242,7 +243,7 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
|
||||||
return nil, fmt.Errorf("unexpected `modulus` for `action=hashmod`: %d; must be greater than 0", modulus)
|
return nil, fmt.Errorf("unexpected `modulus` for `action=hashmod`: %d; must be greater than 0", modulus)
|
||||||
}
|
}
|
||||||
case "keep_metrics":
|
case "keep_metrics":
|
||||||
if rc.Regex == nil || rc.Regex.s == "" {
|
if (rc.Regex == nil || rc.Regex.s == "") && rc.If == nil {
|
||||||
return nil, fmt.Errorf("`regex` must be non-empty for `action=keep_metrics`")
|
return nil, fmt.Errorf("`regex` must be non-empty for `action=keep_metrics`")
|
||||||
}
|
}
|
||||||
if len(sourceLabels) > 0 {
|
if len(sourceLabels) > 0 {
|
||||||
|
@ -251,7 +252,7 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
|
||||||
sourceLabels = []string{"__name__"}
|
sourceLabels = []string{"__name__"}
|
||||||
action = "keep"
|
action = "keep"
|
||||||
case "drop_metrics":
|
case "drop_metrics":
|
||||||
if rc.Regex == nil || rc.Regex.s == "" {
|
if (rc.Regex == nil || rc.Regex.s == "") && rc.If == nil {
|
||||||
return nil, fmt.Errorf("`regex` must be non-empty for `action=drop_metrics`")
|
return nil, fmt.Errorf("`regex` must be non-empty for `action=drop_metrics`")
|
||||||
}
|
}
|
||||||
if len(sourceLabels) > 0 {
|
if len(sourceLabels) > 0 {
|
||||||
|
@ -274,6 +275,7 @@ func parseRelabelConfig(rc *RelabelConfig) (*parsedRelabelConfig, error) {
|
||||||
Modulus: modulus,
|
Modulus: modulus,
|
||||||
Replacement: replacement,
|
Replacement: replacement,
|
||||||
Action: action,
|
Action: action,
|
||||||
|
If: rc.If,
|
||||||
|
|
||||||
regexOriginal: regexOriginalCompiled,
|
regexOriginal: regexOriginalCompiled,
|
||||||
hasCaptureGroupInTargetLabel: strings.Contains(targetLabel, "$"),
|
hasCaptureGroupInTargetLabel: strings.Contains(targetLabel, "$"),
|
||||||
|
|
169
lib/promrelabel/if_expression.go
Normal file
169
lib/promrelabel/if_expression.go
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
package promrelabel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
|
||||||
|
"github.com/VictoriaMetrics/metricsql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IfExpression represents `if` expression at RelabelConfig.
|
||||||
|
//
|
||||||
|
// The `if` expression can contain arbitrary PromQL-like label filters such as `metric_name{filters...}`
|
||||||
|
type IfExpression struct {
|
||||||
|
s string
|
||||||
|
lfs []*labelFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML unmarshals ie from YAML passed to f.
|
||||||
|
func (ie *IfExpression) UnmarshalYAML(f func(interface{}) error) error {
|
||||||
|
var s string
|
||||||
|
if err := f(&s); err != nil {
|
||||||
|
return fmt.Errorf("cannot unmarshal `if` option: %w", err)
|
||||||
|
}
|
||||||
|
expr, err := metricsql.Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot parse `if` series selector: %w", err)
|
||||||
|
}
|
||||||
|
me, ok := expr.(*metricsql.MetricExpr)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("expecting `if` series selector; got %q", expr.AppendString(nil))
|
||||||
|
}
|
||||||
|
lfs, err := metricExprToLabelFilters(me)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot parse `if` filters: %w", err)
|
||||||
|
}
|
||||||
|
ie.s = s
|
||||||
|
ie.lfs = lfs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML marshals ie to YAML.
|
||||||
|
func (ie *IfExpression) MarshalYAML() (interface{}, error) {
|
||||||
|
return ie.s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match returns true if ie matches the given labels.
|
||||||
|
func (ie *IfExpression) Match(labels []prompbmarshal.Label) bool {
|
||||||
|
for _, lf := range ie.lfs {
|
||||||
|
if !lf.match(labels) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func metricExprToLabelFilters(me *metricsql.MetricExpr) ([]*labelFilter, error) {
|
||||||
|
lfs := make([]*labelFilter, len(me.LabelFilters))
|
||||||
|
for i := range me.LabelFilters {
|
||||||
|
lf, err := newLabelFilter(&me.LabelFilters[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot parse %s: %w", me.AppendString(nil), err)
|
||||||
|
}
|
||||||
|
lfs[i] = lf
|
||||||
|
}
|
||||||
|
return lfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// labelFilter contains PromQL filter for `{label op "value"}`
|
||||||
|
type labelFilter struct {
|
||||||
|
label string
|
||||||
|
op string
|
||||||
|
value string
|
||||||
|
|
||||||
|
// re contains compiled regexp for `=~` and `!~` op.
|
||||||
|
re *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLabelFilter(mlf *metricsql.LabelFilter) (*labelFilter, error) {
|
||||||
|
lf := &labelFilter{
|
||||||
|
label: toCanonicalLabelName(mlf.Label),
|
||||||
|
op: getFilterOp(mlf),
|
||||||
|
value: mlf.Value,
|
||||||
|
}
|
||||||
|
if lf.op == "=~" || lf.op == "!~" {
|
||||||
|
// PromQL regexps are anchored by default.
|
||||||
|
// See https://prometheus.io/docs/prometheus/latest/querying/basics/#time-series-selectors
|
||||||
|
reString := "^(?:" + lf.value + ")$"
|
||||||
|
re, err := regexp.Compile(reString)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot parse regexp for %s: %w", mlf.AppendString(nil), err)
|
||||||
|
}
|
||||||
|
lf.re = re
|
||||||
|
}
|
||||||
|
return lf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lf *labelFilter) match(labels []prompbmarshal.Label) bool {
|
||||||
|
switch lf.op {
|
||||||
|
case "=":
|
||||||
|
return lf.equalValue(labels)
|
||||||
|
case "!=":
|
||||||
|
return !lf.equalValue(labels)
|
||||||
|
case "=~":
|
||||||
|
return lf.equalRegexp(labels)
|
||||||
|
case "!~":
|
||||||
|
return !lf.equalRegexp(labels)
|
||||||
|
default:
|
||||||
|
logger.Panicf("BUG: unexpected operation for label filter: %s", lf.op)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lf *labelFilter) equalValue(labels []prompbmarshal.Label) bool {
|
||||||
|
labelNameMatches := 0
|
||||||
|
for _, label := range labels {
|
||||||
|
if toCanonicalLabelName(label.Name) != lf.label {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labelNameMatches++
|
||||||
|
if label.Value == lf.value {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if labelNameMatches == 0 {
|
||||||
|
// Special case for {non_existing_label=""}, which matches anything except of non-empty non_existing_label
|
||||||
|
return lf.value == ""
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lf *labelFilter) equalRegexp(labels []prompbmarshal.Label) bool {
|
||||||
|
labelNameMatches := 0
|
||||||
|
for _, label := range labels {
|
||||||
|
if toCanonicalLabelName(label.Name) != lf.label {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
labelNameMatches++
|
||||||
|
if lf.re.MatchString(label.Value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if labelNameMatches == 0 {
|
||||||
|
// Special case for {non_existing_label=~"something|"}, which matches empty non_existing_label
|
||||||
|
return lf.re.MatchString("")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func toCanonicalLabelName(labelName string) string {
|
||||||
|
if labelName == "__name__" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return labelName
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFilterOp(mlf *metricsql.LabelFilter) string {
|
||||||
|
if mlf.IsNegative {
|
||||||
|
if mlf.IsRegexp {
|
||||||
|
return "!~"
|
||||||
|
}
|
||||||
|
return "!="
|
||||||
|
}
|
||||||
|
if mlf.IsRegexp {
|
||||||
|
return "=~"
|
||||||
|
}
|
||||||
|
return "="
|
||||||
|
}
|
162
lib/promrelabel/if_expression_test.go
Normal file
162
lib/promrelabel/if_expression_test.go
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
package promrelabel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/prompbmarshal"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/protoparser/prometheus"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIfExpressionUnmarshalFailure(t *testing.T) {
|
||||||
|
f := func(s string) {
|
||||||
|
t.Helper()
|
||||||
|
var ie IfExpression
|
||||||
|
err := yaml.UnmarshalStrict([]byte(s), &ie)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expecting non-nil error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(`{`)
|
||||||
|
f(`{x:y}`)
|
||||||
|
f(`[]`)
|
||||||
|
f(`"{"`)
|
||||||
|
f(`'{'`)
|
||||||
|
f(`foo{bar`)
|
||||||
|
f(`foo{bar}`)
|
||||||
|
f(`foo{bar=`)
|
||||||
|
f(`foo{bar="`)
|
||||||
|
f(`foo{bar='`)
|
||||||
|
f(`foo{bar=~"("}`)
|
||||||
|
f(`foo{bar!~"("}`)
|
||||||
|
f(`foo{bar==aaa}`)
|
||||||
|
f(`foo{bar=="b"}`)
|
||||||
|
f(`'foo+bar'`)
|
||||||
|
f(`'foo{bar=~"a[b"}'`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIfExpressionUnmarshalSuccess(t *testing.T) {
|
||||||
|
f := func(s string) {
|
||||||
|
t.Helper()
|
||||||
|
var ie IfExpression
|
||||||
|
if err := yaml.UnmarshalStrict([]byte(s), &ie); err != nil {
|
||||||
|
t.Fatalf("unexpected error during unmarshal: %s", err)
|
||||||
|
}
|
||||||
|
b, err := yaml.Marshal(&ie)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error during marshal: %s", err)
|
||||||
|
}
|
||||||
|
b = bytes.TrimSpace(b)
|
||||||
|
if string(b) != s {
|
||||||
|
t.Fatalf("unexpected marshaled data;\ngot\n%s\nwant\n%s", b, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(`'{}'`)
|
||||||
|
f(`foo`)
|
||||||
|
f(`foo{bar="baz"}`)
|
||||||
|
f(`'{a="b", c!="d", e=~"g", h!~"d"}'`)
|
||||||
|
f(`foo{bar="zs",a=~"b|c"}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIfExpressionMatch(t *testing.T) {
|
||||||
|
f := func(ifExpr, metricWithLabels string) {
|
||||||
|
t.Helper()
|
||||||
|
var ie IfExpression
|
||||||
|
if err := yaml.UnmarshalStrict([]byte(ifExpr), &ie); err != nil {
|
||||||
|
t.Fatalf("unexpected error during unmarshal: %s", err)
|
||||||
|
}
|
||||||
|
labels, err := parseMetricWithLabels(metricWithLabels)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot parse %s: %s", metricWithLabels, err)
|
||||||
|
}
|
||||||
|
if !ie.Match(labels) {
|
||||||
|
t.Fatalf("unexpected mismatch of ifExpr=%s for %s", ifExpr, metricWithLabels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(`foo`, `foo`)
|
||||||
|
f(`foo`, `foo{bar="baz",a="b"}`)
|
||||||
|
f(`foo{bar="a"}`, `foo{bar="a"}`)
|
||||||
|
f(`foo{bar="a"}`, `foo{x="y",bar="a",baz="b"}`)
|
||||||
|
f(`'{a=~"x|abc",y!="z"}'`, `m{x="aa",a="abc"}`)
|
||||||
|
f(`'{a=~"x|abc",y!="z"}'`, `m{x="aa",a="abc",y="qwe"}`)
|
||||||
|
f(`'{__name__="foo"}'`, `foo{bar="baz"}`)
|
||||||
|
f(`'{__name__=~"foo|bar"}'`, `bar`)
|
||||||
|
f(`'{__name__!=""}'`, `foo`)
|
||||||
|
f(`'{__name__!=""}'`, `bar{baz="aa",b="c"}`)
|
||||||
|
f(`'{__name__!~"a.+"}'`, `bar{baz="aa",b="c"}`)
|
||||||
|
f(`foo{a!~"a.+"}`, `foo{a="baa"}`)
|
||||||
|
f(`'{foo=""}'`, `bar`)
|
||||||
|
f(`'{foo!=""}'`, `aa{foo="b"}`)
|
||||||
|
f(`'{foo=~".*"}'`, `abc`)
|
||||||
|
f(`'{foo=~".*"}'`, `abc{foo="bar"}`)
|
||||||
|
f(`'{foo!~".+"}'`, `abc`)
|
||||||
|
f(`'{foo=~"bar|"}'`, `abc`)
|
||||||
|
f(`'{foo=~"bar|"}'`, `abc{foo="bar"}`)
|
||||||
|
f(`'{foo!~"bar|"}'`, `abc{foo="baz"}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIfExpressionMismatch(t *testing.T) {
|
||||||
|
f := func(ifExpr, metricWithLabels string) {
|
||||||
|
t.Helper()
|
||||||
|
var ie IfExpression
|
||||||
|
if err := yaml.UnmarshalStrict([]byte(ifExpr), &ie); err != nil {
|
||||||
|
t.Fatalf("unexpected error during unmarshal: %s", err)
|
||||||
|
}
|
||||||
|
labels, err := parseMetricWithLabels(metricWithLabels)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot parse %s: %s", metricWithLabels, err)
|
||||||
|
}
|
||||||
|
if ie.Match(labels) {
|
||||||
|
t.Fatalf("unexpected match of ifExpr=%s for %s", ifExpr, metricWithLabels)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(`foo`, `bar`)
|
||||||
|
f(`foo`, `a{foo="bar"}`)
|
||||||
|
f(`foo{bar="a"}`, `foo`)
|
||||||
|
f(`foo{bar="a"}`, `foo{bar="b"}`)
|
||||||
|
f(`foo{bar="a"}`, `foo{baz="b",a="b"}`)
|
||||||
|
f(`'{a=~"x|abc",y!="z"}'`, `m{x="aa",a="xabc"}`)
|
||||||
|
f(`'{a=~"x|abc",y!="z"}'`, `m{x="aa",a="abc",y="z"}`)
|
||||||
|
f(`'{__name__!~".+"}'`, `foo`)
|
||||||
|
f(`'{a!~"a.+"}'`, `foo{a="abc"}`)
|
||||||
|
f(`'{foo=""}'`, `bar{foo="aa"}`)
|
||||||
|
f(`'{foo!=""}'`, `aa`)
|
||||||
|
f(`'{foo=~".+"}'`, `abc`)
|
||||||
|
f(`'{foo!~".+"}'`, `abc{foo="x"}`)
|
||||||
|
f(`'{foo=~"bar|"}'`, `abc{foo="baz"}`)
|
||||||
|
f(`'{foo!~"bar|"}'`, `abc`)
|
||||||
|
f(`'{foo!~"bar|"}'`, `abc{foo="bar"}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMetricWithLabels(metricWithLabels string) ([]prompbmarshal.Label, error) {
|
||||||
|
// add a value to metricWithLabels, so it could be parsed by prometheus protocol parser.
|
||||||
|
s := metricWithLabels + " 123"
|
||||||
|
var rows prometheus.Rows
|
||||||
|
var err error
|
||||||
|
rows.UnmarshalWithErrLogger(s, func(s string) {
|
||||||
|
err = fmt.Errorf("error during metric parse: %s", s)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(rows.Rows) != 1 {
|
||||||
|
return nil, fmt.Errorf("unexpected number of rows parsed; got %d; want 1", len(rows.Rows))
|
||||||
|
}
|
||||||
|
r := rows.Rows[0]
|
||||||
|
var lfs []prompbmarshal.Label
|
||||||
|
if r.Metric != "" {
|
||||||
|
lfs = append(lfs, prompbmarshal.Label{
|
||||||
|
Name: "__name__",
|
||||||
|
Value: r.Metric,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, tag := range r.Tags {
|
||||||
|
lfs = append(lfs, prompbmarshal.Label{
|
||||||
|
Name: tag.Key,
|
||||||
|
Value: tag.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return lfs, nil
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ type parsedRelabelConfig struct {
|
||||||
Modulus uint64
|
Modulus uint64
|
||||||
Replacement string
|
Replacement string
|
||||||
Action string
|
Action string
|
||||||
|
If *IfExpression
|
||||||
|
|
||||||
regexOriginal *regexp.Regexp
|
regexOriginal *regexp.Regexp
|
||||||
hasCaptureGroupInTargetLabel bool
|
hasCaptureGroupInTargetLabel bool
|
||||||
|
@ -137,8 +138,17 @@ func FinalizeLabels(dst, src []prompbmarshal.Label) []prompbmarshal.Label {
|
||||||
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config
|
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config
|
||||||
func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset int) []prompbmarshal.Label {
|
func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset int) []prompbmarshal.Label {
|
||||||
src := labels[labelsOffset:]
|
src := labels[labelsOffset:]
|
||||||
|
if prc.If != nil && !prc.If.Match(labels) {
|
||||||
|
if prc.Action == "keep" {
|
||||||
|
// Drop the target on `if` mismatch for `action: keep`
|
||||||
|
return labels[:labelsOffset]
|
||||||
|
}
|
||||||
|
// Do not apply prc actions on `if` mismatch.
|
||||||
|
return labels
|
||||||
|
}
|
||||||
switch prc.Action {
|
switch prc.Action {
|
||||||
case "replace":
|
case "replace":
|
||||||
|
// Store `replacement` at `target_label` if the `regex` matches `source_labels` joined with `separator`
|
||||||
bb := relabelBufPool.Get()
|
bb := relabelBufPool.Get()
|
||||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||||
if prc.Regex == defaultRegexForRelabelConfig && !prc.hasCaptureGroupInTargetLabel {
|
if prc.Regex == defaultRegexForRelabelConfig && !prc.hasCaptureGroupInTargetLabel {
|
||||||
|
@ -174,6 +184,8 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
||||||
relabelBufPool.Put(bb)
|
relabelBufPool.Put(bb)
|
||||||
return setLabelValue(labels, labelsOffset, nameStr, valueStr)
|
return setLabelValue(labels, labelsOffset, nameStr, valueStr)
|
||||||
case "replace_all":
|
case "replace_all":
|
||||||
|
// Replace all the occurences of `regex` at `source_labels` joined with `separator` with the `replacement`
|
||||||
|
// and store the result at `target_label`
|
||||||
bb := relabelBufPool.Get()
|
bb := relabelBufPool.Get()
|
||||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||||
sourceStr := string(bb.B)
|
sourceStr := string(bb.B)
|
||||||
|
@ -208,6 +220,15 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
||||||
}
|
}
|
||||||
return labels
|
return labels
|
||||||
case "keep":
|
case "keep":
|
||||||
|
// Keep the target if `source_labels` joined with `separator` match the `regex`.
|
||||||
|
if prc.Regex == defaultRegexForRelabelConfig {
|
||||||
|
// Fast path for the case with `if` and without explicitly set `regex`:
|
||||||
|
//
|
||||||
|
// - action: keep
|
||||||
|
// if: 'some{label=~"filters"}'
|
||||||
|
//
|
||||||
|
return labels
|
||||||
|
}
|
||||||
bb := relabelBufPool.Get()
|
bb := relabelBufPool.Get()
|
||||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||||
keep := prc.matchString(bytesutil.ToUnsafeString(bb.B))
|
keep := prc.matchString(bytesutil.ToUnsafeString(bb.B))
|
||||||
|
@ -217,6 +238,15 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
||||||
}
|
}
|
||||||
return labels
|
return labels
|
||||||
case "drop":
|
case "drop":
|
||||||
|
// Drop the target if `source_labels` joined with `separator` don't match the `regex`.
|
||||||
|
if prc.Regex == defaultRegexForRelabelConfig {
|
||||||
|
// Fast path for the case with `if` and without explicitly set `regex`:
|
||||||
|
//
|
||||||
|
// - action: drop
|
||||||
|
// if: 'some{label=~"filters"}'
|
||||||
|
//
|
||||||
|
return labels[:labelsOffset]
|
||||||
|
}
|
||||||
bb := relabelBufPool.Get()
|
bb := relabelBufPool.Get()
|
||||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||||
drop := prc.matchString(bytesutil.ToUnsafeString(bb.B))
|
drop := prc.matchString(bytesutil.ToUnsafeString(bb.B))
|
||||||
|
@ -226,6 +256,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
||||||
}
|
}
|
||||||
return labels
|
return labels
|
||||||
case "hashmod":
|
case "hashmod":
|
||||||
|
// Calculate the `modulus` from the hash of `source_labels` joined with `separator` and store it at `target_label`
|
||||||
bb := relabelBufPool.Get()
|
bb := relabelBufPool.Get()
|
||||||
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
bb.B = concatLabelValues(bb.B[:0], src, prc.SourceLabels, prc.Separator)
|
||||||
h := xxhash.Sum64(bb.B) % prc.Modulus
|
h := xxhash.Sum64(bb.B) % prc.Modulus
|
||||||
|
@ -233,6 +264,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
||||||
relabelBufPool.Put(bb)
|
relabelBufPool.Put(bb)
|
||||||
return setLabelValue(labels, labelsOffset, prc.TargetLabel, value)
|
return setLabelValue(labels, labelsOffset, prc.TargetLabel, value)
|
||||||
case "labelmap":
|
case "labelmap":
|
||||||
|
// Replace label names with the `replacement` if they match `regex`
|
||||||
for i := range src {
|
for i := range src {
|
||||||
label := &src[i]
|
label := &src[i]
|
||||||
labelName, ok := prc.replaceFullString(label.Name, prc.Replacement, prc.hasCaptureGroupInReplacement)
|
labelName, ok := prc.replaceFullString(label.Name, prc.Replacement, prc.hasCaptureGroupInReplacement)
|
||||||
|
@ -242,12 +274,14 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
||||||
}
|
}
|
||||||
return labels
|
return labels
|
||||||
case "labelmap_all":
|
case "labelmap_all":
|
||||||
|
// Replace all the occurences of `regex` at label names with `replacement`
|
||||||
for i := range src {
|
for i := range src {
|
||||||
label := &src[i]
|
label := &src[i]
|
||||||
label.Name, _ = prc.replaceStringSubmatches(label.Name, prc.Replacement, prc.hasCaptureGroupInReplacement)
|
label.Name, _ = prc.replaceStringSubmatches(label.Name, prc.Replacement, prc.hasCaptureGroupInReplacement)
|
||||||
}
|
}
|
||||||
return labels
|
return labels
|
||||||
case "labeldrop":
|
case "labeldrop":
|
||||||
|
// Drop labels with names matching the `regex`
|
||||||
dst := labels[:labelsOffset]
|
dst := labels[:labelsOffset]
|
||||||
for i := range src {
|
for i := range src {
|
||||||
label := &src[i]
|
label := &src[i]
|
||||||
|
@ -257,6 +291,7 @@ func (prc *parsedRelabelConfig) apply(labels []prompbmarshal.Label, labelsOffset
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
case "labelkeep":
|
case "labelkeep":
|
||||||
|
// Keep labels with names matching the `regex`
|
||||||
dst := labels[:labelsOffset]
|
dst := labels[:labelsOffset]
|
||||||
for i := range src {
|
for i := range src {
|
||||||
label := &src[i]
|
label := &src[i]
|
||||||
|
|
|
@ -134,6 +134,25 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
source_labels: ["foo"]
|
source_labels: ["foo"]
|
||||||
target_label: "bar"
|
target_label: "bar"
|
||||||
regex: ".+"
|
regex: ".+"
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("replace-if-miss", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: replace
|
||||||
|
if: '{foo="bar"}'
|
||||||
|
source_labels: ["xxx", "foo"]
|
||||||
|
target_label: "bar"
|
||||||
|
replacement: "a-$1-b"
|
||||||
`, []prompbmarshal.Label{
|
`, []prompbmarshal.Label{
|
||||||
{
|
{
|
||||||
Name: "xxx",
|
Name: "xxx",
|
||||||
|
@ -152,6 +171,29 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
source_labels: ["xxx", "foo"]
|
source_labels: ["xxx", "foo"]
|
||||||
target_label: "bar"
|
target_label: "bar"
|
||||||
replacement: "a-$1-b"
|
replacement: "a-$1-b"
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Value: "a-yyy;-b",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("replace-if-hit", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: replace
|
||||||
|
if: '{xxx=~".y."}'
|
||||||
|
source_labels: ["xxx", "foo"]
|
||||||
|
target_label: "bar"
|
||||||
|
replacement: "a-$1-b"
|
||||||
`, []prompbmarshal.Label{
|
`, []prompbmarshal.Label{
|
||||||
{
|
{
|
||||||
Name: "xxx",
|
Name: "xxx",
|
||||||
|
@ -333,6 +375,26 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
t.Run("replace_all-if-miss", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: replace_all
|
||||||
|
if: 'foo'
|
||||||
|
source_labels: ["xxx"]
|
||||||
|
target_label: "xxx"
|
||||||
|
regex: "-"
|
||||||
|
replacement: "."
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "a-b-c",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "a-b-c",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
t.Run("replace_all-hit", func(t *testing.T) {
|
t.Run("replace_all-hit", func(t *testing.T) {
|
||||||
f(`
|
f(`
|
||||||
- action: replace_all
|
- action: replace_all
|
||||||
|
@ -340,6 +402,26 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
target_label: "xxx"
|
target_label: "xxx"
|
||||||
regex: "-"
|
regex: "-"
|
||||||
replacement: "."
|
replacement: "."
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "a-b-c",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "a.b.c",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("replace_all-if-hit", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: replace_all
|
||||||
|
if: '{non_existing_label=~".*"}'
|
||||||
|
source_labels: ["xxx"]
|
||||||
|
target_label: "xxx"
|
||||||
|
regex: "-"
|
||||||
|
replacement: "."
|
||||||
`, []prompbmarshal.Label{
|
`, []prompbmarshal.Label{
|
||||||
{
|
{
|
||||||
Name: "xxx",
|
Name: "xxx",
|
||||||
|
@ -530,6 +612,33 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
},
|
},
|
||||||
}, true, []prompbmarshal.Label{})
|
}, true, []prompbmarshal.Label{})
|
||||||
})
|
})
|
||||||
|
t.Run("keep-if-miss", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: keep
|
||||||
|
if: '{foo="bar"}'
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{})
|
||||||
|
})
|
||||||
|
t.Run("keep-if-hit", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: keep
|
||||||
|
if: '{foo="yyy"}'
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
t.Run("keep-hit", func(t *testing.T) {
|
t.Run("keep-hit", func(t *testing.T) {
|
||||||
f(`
|
f(`
|
||||||
- action: keep
|
- action: keep
|
||||||
|
@ -577,6 +686,33 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
},
|
},
|
||||||
}, true, []prompbmarshal.Label{})
|
}, true, []prompbmarshal.Label{})
|
||||||
})
|
})
|
||||||
|
t.Run("keep_metrics-if-miss", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: keep_metrics
|
||||||
|
if: 'bar'
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "__name__",
|
||||||
|
Value: "foo",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{})
|
||||||
|
})
|
||||||
|
t.Run("keep_metrics-if-hit", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: keep_metrics
|
||||||
|
if: 'foo'
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "__name__",
|
||||||
|
Value: "foo",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "__name__",
|
||||||
|
Value: "foo",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
t.Run("keep_metrics-hit", func(t *testing.T) {
|
t.Run("keep_metrics-hit", func(t *testing.T) {
|
||||||
f(`
|
f(`
|
||||||
- action: keep_metrics
|
- action: keep_metrics
|
||||||
|
@ -617,6 +753,33 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
t.Run("drop-if-miss", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: drop
|
||||||
|
if: '{foo="bar"}'
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("drop-if-hit", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: drop
|
||||||
|
if: '{foo="yyy"}'
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{})
|
||||||
|
})
|
||||||
t.Run("drop-hit", func(t *testing.T) {
|
t.Run("drop-hit", func(t *testing.T) {
|
||||||
f(`
|
f(`
|
||||||
- action: drop
|
- action: drop
|
||||||
|
@ -659,6 +822,33 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
t.Run("drop_metrics-if-miss", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: drop_metrics
|
||||||
|
if: bar
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "__name__",
|
||||||
|
Value: "foo",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "__name__",
|
||||||
|
Value: "foo",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("drop_metrics-if-hit", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: drop_metrics
|
||||||
|
if: foo
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "__name__",
|
||||||
|
Value: "foo",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{})
|
||||||
|
})
|
||||||
t.Run("drop_metrics-hit", func(t *testing.T) {
|
t.Run("drop_metrics-hit", func(t *testing.T) {
|
||||||
f(`
|
f(`
|
||||||
- action: drop_metrics
|
- action: drop_metrics
|
||||||
|
@ -694,6 +884,48 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
t.Run("hashmod-if-miss", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: hashmod
|
||||||
|
if: '{foo="bar"}'
|
||||||
|
source_labels: [foo]
|
||||||
|
target_label: aaa
|
||||||
|
modulus: 123
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("hashmod-if-hit", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: hashmod
|
||||||
|
if: '{foo="yyy"}'
|
||||||
|
source_labels: [foo]
|
||||||
|
target_label: aaa
|
||||||
|
modulus: 123
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "aaa",
|
||||||
|
Value: "73",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
t.Run("hashmod-hit", func(t *testing.T) {
|
t.Run("hashmod-hit", func(t *testing.T) {
|
||||||
f(`
|
f(`
|
||||||
- action: hashmod
|
- action: hashmod
|
||||||
|
@ -716,6 +948,62 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
t.Run("labelmap-copy-label-if-miss", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: labelmap
|
||||||
|
if: '{foo="yyy",foobar="aab"}'
|
||||||
|
regex: "foo"
|
||||||
|
replacement: "bar"
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foobar",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foobar",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("labelmap-copy-label-if-hit", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: labelmap
|
||||||
|
if: '{foo="yyy",foobar="aaa"}'
|
||||||
|
regex: "foo"
|
||||||
|
replacement: "bar"
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foobar",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foobar",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
t.Run("labelmap-copy-label", func(t *testing.T) {
|
t.Run("labelmap-copy-label", func(t *testing.T) {
|
||||||
f(`
|
f(`
|
||||||
- action: labelmap
|
- action: labelmap
|
||||||
|
@ -830,6 +1118,58 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
t.Run("labelmap_all-if-miss", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: labelmap_all
|
||||||
|
if: foobar
|
||||||
|
regex: "\\."
|
||||||
|
replacement: "-"
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo.bar.baz",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foobar",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo.bar.baz",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foobar",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("labelmap_all-if-hit", func(t *testing.T) {
|
||||||
|
f(`
|
||||||
|
- action: labelmap_all
|
||||||
|
if: '{foo.bar.baz="yyy"}'
|
||||||
|
regex: "\\."
|
||||||
|
replacement: "-"
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo.bar.baz",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foobar",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
}, true, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo-bar-baz",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foobar",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
t.Run("labelmap_all", func(t *testing.T) {
|
t.Run("labelmap_all", func(t *testing.T) {
|
||||||
f(`
|
f(`
|
||||||
- action: labelmap_all
|
- action: labelmap_all
|
||||||
|
@ -895,6 +1235,66 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
Value: "bbb",
|
Value: "bbb",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
// if-miss
|
||||||
|
f(`
|
||||||
|
- action: labeldrop
|
||||||
|
if: foo
|
||||||
|
regex: dropme
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "dropme",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "bar",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "dropme",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// if-hit
|
||||||
|
f(`
|
||||||
|
- action: labeldrop
|
||||||
|
if: '{xxx="yyy"}'
|
||||||
|
regex: dropme
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "dropme",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "bar",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Value: "bar",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "xxx",
|
||||||
|
Value: "yyy",
|
||||||
|
},
|
||||||
|
})
|
||||||
f(`
|
f(`
|
||||||
- action: labeldrop
|
- action: labeldrop
|
||||||
regex: dropme
|
regex: dropme
|
||||||
|
@ -1059,6 +1459,62 @@ func TestApplyRelabelConfigs(t *testing.T) {
|
||||||
Value: "aaa",
|
Value: "aaa",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
// if-miss
|
||||||
|
f(`
|
||||||
|
- action: labelkeep
|
||||||
|
if: '{aaaa="awefx"}'
|
||||||
|
regex: keepme
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "keepme",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "aaaa",
|
||||||
|
Value: "awef",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "keepme-aaa",
|
||||||
|
Value: "234",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "aaaa",
|
||||||
|
Value: "awef",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "keepme",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "keepme-aaa",
|
||||||
|
Value: "234",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// if-hit
|
||||||
|
f(`
|
||||||
|
- action: labelkeep
|
||||||
|
if: '{aaaa="awef"}'
|
||||||
|
regex: keepme
|
||||||
|
`, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "keepme",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "aaaa",
|
||||||
|
Value: "awef",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "keepme-aaa",
|
||||||
|
Value: "234",
|
||||||
|
},
|
||||||
|
}, false, []prompbmarshal.Label{
|
||||||
|
{
|
||||||
|
Name: "keepme",
|
||||||
|
Value: "aaa",
|
||||||
|
},
|
||||||
|
})
|
||||||
f(`
|
f(`
|
||||||
- action: labelkeep
|
- action: labelkeep
|
||||||
regex: keepme
|
regex: keepme
|
||||||
|
|
Loading…
Reference in a new issue