Skip to content

Commit

Permalink
feat: add negativeRegexp to hasSourceTenantsForMetrics (#91)
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Chodur <[email protected]>
  • Loading branch information
FUSAKLA authored Oct 30, 2024
1 parent 92ad3ad commit 5ef1076
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Changed: The cache format is now scoped by source tenants (internal change, no action required)
- Changed: Logging uses structured logs now
- Added: new `httpHeaders` field in the `prometheus` section of the config to set custom headers in the Prometheus requests
- Added: new option `negativeRegexp` to the `hasSourceTenantsForMetrics` validation, see [the docs](docs/validations.md#hassourcetenantsformetrics)

## [3.3.0] - 2024-10-03
- Changed: Upgrade to go 1.23
Expand Down
8 changes: 7 additions & 1 deletion docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,15 +401,21 @@ params:
sourceTenants:
<tenant_name>:
- regexp: <metric_name_regexp> # The regexp will be fully anchored (surrounded by ^...$)
negativeRegexp: <metric_name_regexp> # Optional, metrics matching the regexp will be excluded from the check, will be fully anchored (surrounded by ^...$)
description: <description> # Optional, will be shown in the validator output human-readable description
# Example:
# k8s:
# - regexp: "kube_.*|container_.*"
# description: "Metrics from KSM"
# - regexp: "container_.*"
# description: "Metrics from cAdvisor "
# description: "Metrics from cAdvisor"
# - regexp: "kafka_.*"
# - regexp: "node_.*"
# description: "Node exporter metrics provided by the k8s infrastructure team"
# kafka:
# - regexp: "kafka_.*"
# negativeRegexp: "kafka_(consumer|producer)_.*"
# description: "Metrics from Kafka"
```

## Alert validators
Expand Down
1 change: 1 addition & 0 deletions examples/mimir/validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ validationRules:
sourceTenants:
"k8s":
- regexp: "container_.*"
negativeRegexp: "container_memory_.*"
description: "Metrics from cAdvisor"
29 changes: 21 additions & 8 deletions pkg/validator/others.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import (
)

type SourceTenantMetrics struct {
Regexp string `yaml:"regexp"`
Description string `yaml:"description"`
Regexp string `yaml:"regexp"`
NegativeRegexp string `yaml:"negativeRegexp"`
Description string `yaml:"description"`
}

func newHasSourceTenantsForMetrics(paramsConfig yaml.Node) (Validator, error) {
Expand All @@ -32,13 +33,21 @@ func newHasSourceTenantsForMetrics(paramsConfig yaml.Node) (Validator, error) {
for tenant, metrics := range params.SourceTenants {
m := make([]tenantMetrics, len(metrics))
for i, metric := range metrics {
compiledRegexp, err := regexp.Compile("^" + metric.Regexp + "$")
compiledRegexp, err := compileAnchoredRegexp(metric.Regexp)
if err != nil {
return nil, fmt.Errorf("invalid metric name regexp: %s", metric.Regexp)
return nil, fmt.Errorf("invalid metric name regexp: %s", anchorRegexp(metric.Regexp))
}
compiledNegativeRegexp := (*regexp.Regexp)(nil)
if metric.NegativeRegexp != "" {
compiledNegativeRegexp, err = compileAnchoredRegexp(metric.NegativeRegexp)
if err != nil {
return nil, fmt.Errorf("invalid metric name regexp: %s", anchorRegexp(metric.NegativeRegexp))
}
}
m[i] = tenantMetrics{
regexp: compiledRegexp,
description: metric.Description,
regexp: compiledRegexp,
negativeRegexp: compiledNegativeRegexp,
description: metric.Description,
}
}
validator.sourceTenants[tenant] = m
Expand All @@ -47,8 +56,9 @@ func newHasSourceTenantsForMetrics(paramsConfig yaml.Node) (Validator, error) {
}

type tenantMetrics struct {
regexp *regexp.Regexp
description string
regexp *regexp.Regexp
negativeRegexp *regexp.Regexp
description string
}

type hasSourceTenantsForMetrics struct {
Expand Down Expand Up @@ -84,6 +94,9 @@ func (h hasSourceTenantsForMetrics) Validate(group unmarshaler.RuleGroup, rule r
if !metric.regexp.MatchString(usedMetric.Name) {
continue
}
if metric.negativeRegexp != nil && metric.negativeRegexp.MatchString(usedMetric.Name) {
continue
}
if len(group.SourceTenants) == 0 && h.defaultTenant == tenant {
continue
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/validator/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package validator

import "fmt"

func anchorRegexp(regexp string) string {
return fmt.Sprintf("^%s$", regexp)
}
2 changes: 1 addition & 1 deletion pkg/validator/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ type Validator interface {
}

func compileAnchoredRegexp(regexpString string) (*regexp.Regexp, error) {
return regexp.Compile("^" + regexpString + "$")
return regexp.Compile(anchorRegexp(regexpString))
}
4 changes: 3 additions & 1 deletion pkg/validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
)

func mustCompileAnchoredRegexp(regexpString string) *regexp.Regexp {
compiled, err := regexp.Compile("^" + regexpString + "$")
compiled, err := compileAnchoredRegexp(regexpString)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -227,6 +227,8 @@ var testCases = []struct {
{name: "usesMetricWithSourceTenantAndGroupHasMultipleSourceTenantsAndOneIsMissing", validator: hasSourceTenantsForMetrics{sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant2", "tenant3"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 1},
{name: "doesNotHaveSourceTenantForMetricButIsDefault", validator: hasSourceTenantsForMetrics{defaultTenant: "tenant1", sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 0},
{name: "doesNotHaveSourceTenantForMetricAndIsNotDefault", validator: hasSourceTenantsForMetrics{defaultTenant: "tenant2", sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 1},
{name: "notMatchingNegativeRegexp", validator: hasSourceTenantsForMetrics{sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric_.*$`), negativeRegexp: regexp.MustCompile(`^teanant1_metric_bar$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant1"}}, rule: rulefmt.Rule{Expr: `teanant1_metric_foo{foo="bar"}`}, expectedErrors: 0},
{name: "MatchingNegativeRegexp", validator: hasSourceTenantsForMetrics{sourceTenants: map[string][]tenantMetrics{"tenant1": {{regexp: regexp.MustCompile(`^teanant1_metric_.*$`), negativeRegexp: regexp.MustCompile(`^teanant1_metric_bar$`)}}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{}}, rule: rulefmt.Rule{Expr: `teanant1_metric_bar{foo="bar"}`}, expectedErrors: 0},

// hasAllowedSourceTenants
{name: "emptyAllowedSourceTenantsAndGroupSourceTenants", validator: hasAllowedSourceTenants{allowedSourceTenants: []string{}}, group: unmarshaler.RuleGroup{SourceTenants: []string{}}, rule: rulefmt.Rule{Expr: `up{foo="bar"}`}, expectedErrors: 0},
Expand Down

0 comments on commit 5ef1076

Please sign in to comment.