Add 'hashmod' relabel action.
This takes the modulus of a hash of some labels. Combined with a keep relabel action, this allows for sharding of targets across multiple prometheus servers.
This commit is contained in:
parent
30fc04b240
commit
682f949ab1
|
@ -455,9 +455,11 @@ const (
|
||||||
// Performs a regex replacement.
|
// Performs a regex replacement.
|
||||||
RelabelReplace RelabelAction = "replace"
|
RelabelReplace RelabelAction = "replace"
|
||||||
// Drops targets for which the input does not match the regex.
|
// Drops targets for which the input does not match the regex.
|
||||||
RelabelKeep = "keep"
|
RelabelKeep RelabelAction = "keep"
|
||||||
// Drops targets for which the input does match the regex.
|
// Drops targets for which the input does match the regex.
|
||||||
RelabelDrop = "drop"
|
RelabelDrop RelabelAction = "drop"
|
||||||
|
// Sets a label to the modulus of a hash of labels.
|
||||||
|
RelabelHashMod RelabelAction = "hashmod"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||||
|
@ -467,7 +469,7 @@ func (a *RelabelAction) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switch act := RelabelAction(strings.ToLower(s)); act {
|
switch act := RelabelAction(strings.ToLower(s)); act {
|
||||||
case RelabelReplace, RelabelKeep, RelabelDrop:
|
case RelabelReplace, RelabelKeep, RelabelDrop, RelabelHashMod:
|
||||||
*a = act
|
*a = act
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -482,7 +484,9 @@ type RelabelConfig struct {
|
||||||
// Separator is the string between concatenated values from the source labels.
|
// Separator is the string between concatenated values from the source labels.
|
||||||
Separator string `yaml:"separator,omitempty"`
|
Separator string `yaml:"separator,omitempty"`
|
||||||
// Regex against which the concatenation is matched.
|
// Regex against which the concatenation is matched.
|
||||||
Regex *Regexp `yaml:"regex"`
|
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.
|
// The label to which the resulting string is written in a replacement.
|
||||||
TargetLabel clientmodel.LabelName `yaml:"target_label,omitempty"`
|
TargetLabel clientmodel.LabelName `yaml:"target_label,omitempty"`
|
||||||
// Replacement is the regex replacement pattern to be used.
|
// Replacement is the regex replacement pattern to be used.
|
||||||
|
@ -501,9 +505,12 @@ func (c *RelabelConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
if err := unmarshal((*plain)(c)); err != nil {
|
if err := unmarshal((*plain)(c)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if c.Regex == nil {
|
if c.Regex == nil && c.Action != RelabelHashMod {
|
||||||
return fmt.Errorf("relabel configuration requires a regular expression")
|
return fmt.Errorf("relabel configuration requires a regular expression")
|
||||||
}
|
}
|
||||||
|
if c.Modulus == 0 && c.Action == RelabelHashMod {
|
||||||
|
return fmt.Errorf("relabel configuration for hashmod requires non-zero modulus")
|
||||||
|
}
|
||||||
return checkOverflow(c.XXX, "relabel_config")
|
return checkOverflow(c.XXX, "relabel_config")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,8 +534,11 @@ func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalYAML implements the yaml.Marshaler interface.
|
// MarshalYAML implements the yaml.Marshaler interface.
|
||||||
func (re Regexp) MarshalYAML() (interface{}, error) {
|
func (re *Regexp) MarshalYAML() (interface{}, error) {
|
||||||
return re.String(), nil
|
if re != nil {
|
||||||
|
return re.String(), nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duration encapsulates a time.Duration and makes it YAML marshallable.
|
// Duration encapsulates a time.Duration and makes it YAML marshallable.
|
||||||
|
|
|
@ -114,6 +114,19 @@ var expectedConf = &Config{
|
||||||
Separator: ";",
|
Separator: ";",
|
||||||
Action: RelabelDrop,
|
Action: RelabelDrop,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
SourceLabels: clientmodel.LabelNames{"__address__"},
|
||||||
|
TargetLabel: "__hash",
|
||||||
|
Modulus: 8,
|
||||||
|
Separator: ";",
|
||||||
|
Action: RelabelHashMod,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SourceLabels: clientmodel.LabelNames{"__hash"},
|
||||||
|
Regex: &Regexp{*regexp.MustCompile("^1$")},
|
||||||
|
Separator: ";",
|
||||||
|
Action: RelabelKeep,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
MetricRelabelConfigs: []*RelabelConfig{
|
MetricRelabelConfigs: []*RelabelConfig{
|
||||||
{
|
{
|
||||||
|
@ -194,6 +207,9 @@ var expectedErrors = []struct {
|
||||||
}, {
|
}, {
|
||||||
filename: "regex_missing.bad.yml",
|
filename: "regex_missing.bad.yml",
|
||||||
errMsg: "relabel configuration requires a regular expression",
|
errMsg: "relabel configuration requires a regular expression",
|
||||||
|
}, {
|
||||||
|
filename: "modulus_missing.bad.yml",
|
||||||
|
errMsg: "relabel configuration for hashmod requires non-zero modulus",
|
||||||
}, {
|
}, {
|
||||||
filename: "rules.bad.yml",
|
filename: "rules.bad.yml",
|
||||||
errMsg: "invalid rule file path",
|
errMsg: "invalid rule file path",
|
||||||
|
|
|
@ -71,6 +71,13 @@ scrape_configs:
|
||||||
- source_labels: [job]
|
- source_labels: [job]
|
||||||
regex: (.*)some-[regex]$
|
regex: (.*)some-[regex]$
|
||||||
action: drop
|
action: drop
|
||||||
|
- source_labels: [__address__]
|
||||||
|
modulus: 8
|
||||||
|
target_label: __hash
|
||||||
|
action: hashmod
|
||||||
|
- source_labels: [__hash]
|
||||||
|
regex: ^1$
|
||||||
|
action: keep
|
||||||
|
|
||||||
metric_relabel_configs:
|
metric_relabel_configs:
|
||||||
- source_labels: [__name__]
|
- source_labels: [__name__]
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
scrape_configs:
|
||||||
|
- job_name: prometheus
|
||||||
|
relabel_configs:
|
||||||
|
- regex: abcdef
|
||||||
|
action: hashmod
|
|
@ -2,6 +2,7 @@ package retrieval
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"hash/fnv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
clientmodel "github.com/prometheus/client_golang/model"
|
clientmodel "github.com/prometheus/client_golang/model"
|
||||||
|
@ -56,6 +57,11 @@ func relabel(labels clientmodel.LabelSet, cfg *config.RelabelConfig) (clientmode
|
||||||
} else {
|
} else {
|
||||||
labels[cfg.TargetLabel] = clientmodel.LabelValue(res)
|
labels[cfg.TargetLabel] = clientmodel.LabelValue(res)
|
||||||
}
|
}
|
||||||
|
case config.RelabelHashMod:
|
||||||
|
hasher := fnv.New64a()
|
||||||
|
hasher.Write([]byte(val))
|
||||||
|
mod := hasher.Sum64() % cfg.Modulus
|
||||||
|
labels[cfg.TargetLabel] = clientmodel.LabelValue(fmt.Sprintf("%d", mod))
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("retrieval.relabel: unknown relabel action type %q", cfg.Action))
|
panic(fmt.Errorf("retrieval.relabel: unknown relabel action type %q", cfg.Action))
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,6 +151,28 @@ func TestRelabel(t *testing.T) {
|
||||||
"a": "boo",
|
"a": "boo",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: clientmodel.LabelSet{
|
||||||
|
"a": "foo",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "baz",
|
||||||
|
},
|
||||||
|
relabel: []*config.RelabelConfig{
|
||||||
|
{
|
||||||
|
SourceLabels: clientmodel.LabelNames{"c"},
|
||||||
|
TargetLabel: clientmodel.LabelName("d"),
|
||||||
|
Separator: ";",
|
||||||
|
Action: config.RelabelHashMod,
|
||||||
|
Modulus: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: clientmodel.LabelSet{
|
||||||
|
"a": "foo",
|
||||||
|
"b": "bar",
|
||||||
|
"c": "baz",
|
||||||
|
"d": "58",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
|
|
Loading…
Reference in New Issue