Skip to content

Commit

Permalink
feat: change the params of the hasSourceTenantsForMetrics validator
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Chodur <m.chodur@seznam.cz>
  • Loading branch information
FUSAKLA committed Mar 4, 2024
1 parent 5a20a17 commit cb50345
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 23 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
expressionDoesNotUseIrate: you should not use the `irate` function in rules, for more info see https://www.robustperception.io/avoid-irate-in-alerts/ (Just do as I say!)
```

- :warning: CHANGED: Params of the `hasSourceTenantsForMetrics` validator. See its [docs](docs/validations.md#hassourcetenantsformetrics).

## [v2.9.0] - 2024-03-02
- Added new `All rules` validator `expressionIsWellFormatted` to check if rules are well formatted as `promtool promql format` would do.
- Added new `Group` validator `maxRulesPerGroup` to check if the number of rules in the group is not exceeding the limit.
Expand Down
8 changes: 6 additions & 2 deletions docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,13 @@ Fails, if the rule uses metric, that matches the specified regular expression fo
```yaml
params:
sourceTenants:
<tenant_name>: <metric_name_regexp> # The regexp will be fully anchored (surrounded by ^...$)
<tenant_name>:
regexp: <metric_name_regexp> # The regexp will be fully anchored (surrounded by ^...$)
description: <description> # Optional, will be shown in the validator output human-readable description
# Example:
# k8s: "kube_.*|container_.*"
# k8s:
# regexp: "kube_.*|container_.*"
# description: "Kubernetes metrics from KSM and cAdvisor provided by the k8s infrastructure team"
```

## Alert validators
Expand Down
4 changes: 3 additions & 1 deletion examples/human_readable.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ <h2><a href="#check-prometheus-limitations">check-prometheus-limitations</a></h2

<h2><a href="#check-source-tenants">check-source-tenants</a></h2>
<ul>
<li>All rules rule group, the rule belongs to, has the required <code>source_tenants</code> configured, according to the mapping of metric names to tenants: <code>k8s</code>:<code>^container_.*|kube_.*$</code></li>
<li>All rules rule group, the rule belongs to, has the required <code>source_tenants</code> configured, according to the mapping of metric names to tenants:
<br/> <code>mysql</code>: <code>^mysql_.*$</code> (MySQL metrics from the MySQL team)
<br/> <code>k8s</code>: <code>^container_.*|kube_.*$</code> (Kubernetes metrics from KSM and cAdvisor provided by the k8s infrastructure team)</li>
</ul>

<h2><a href="#check-metric-name">check-metric-name</a></h2>
Expand Down
4 changes: 3 additions & 1 deletion examples/human_readable.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ Validation rules:
- All rules does not use any of the `cluster`,`locality`,`prometheus-type`,`replica` labels is in its expression

check-source-tenants
- All rules rule group, the rule belongs to, has the required `source_tenants` configured, according to the mapping of metric names to tenants: `k8s`:`^container_.*|kube_.*$`
- All rules rule group, the rule belongs to, has the required `source_tenants` configured, according to the mapping of metric names to tenants:
`mysql`: `^mysql_.*$` (MySQL metrics from the MySQL team)
`k8s`: `^container_.*|kube_.*$` (Kubernetes metrics from KSM and cAdvisor provided by the k8s infrastructure team)

check-metric-name
- Alert expression uses metric name in selectors
Expand Down
7 changes: 6 additions & 1 deletion examples/validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,12 @@ validationRules:
- type: hasSourceTenantsForMetrics
params:
sourceTenants:
k8s: "container_.*|kube_.*"
"k8s":
regexp: "container_.*|kube_.*"
description: "Kubernetes metrics from KSM and cAdvisor provided by the k8s infrastructure team"
"mysql":
regexp: "mysql_.*"
description: "MySQL metrics from the MySQL team"

- name: check-metric-name
scope: Alert
Expand Down
12 changes: 9 additions & 3 deletions pkg/report/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var htmlTemplate = `
<h2><a href="#{{.Name}}">{{.Name}}</a></h2>
<ul>
{{- range .Validations }}
<li>{{$currentRule.Scope}} {{. | backticksToCodeTag }}</li>
<li>{{$currentRule.Scope}} {{. | backticksToCodeTag | indentedToNewLines | escape }}</li>
{{- end }}
</ul>
{{- end }}
Expand Down Expand Up @@ -43,8 +43,14 @@ Validation rules:
`

var customFuncs = template.FuncMap{
"backticksToCodeTag": func(s string) template.HTML {
return template.HTML(regexp.MustCompile("`([^`]+)`").ReplaceAllString(s, "<code>$1</code>"))
"backticksToCodeTag": func(s string) string {
return regexp.MustCompile("`([^`]+)`").ReplaceAllString(s, "<code>$1</code>")
},
"indentedToNewLines": func(s string) string {
return regexp.MustCompile(`( {4,})`).ReplaceAllString(s, "<br/>$1")
},
"escape": func(s string) template.HTML {
return template.HTML(s)
},
}

Expand Down
33 changes: 23 additions & 10 deletions pkg/validator/others.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,50 @@ import (
"gopkg.in/yaml.v3"
)

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

func newHasSourceTenantsForMetrics(paramsConfig yaml.Node) (Validator, error) {
params := struct {
SourceTenants map[string]string `yaml:"sourceTenants"`
SourceTenants map[string]SourceTenantMetrics `yaml:"sourceTenants"`
}{}
if err := paramsConfig.Decode(&params); err != nil {
return nil, err
}
if params.SourceTenants == nil || len(params.SourceTenants) == 0 {
return nil, fmt.Errorf("sourceTenants metrics mapping needs to be set")
}
validator := hasSourceTenantsForMetrics{sourceTenants: map[string]*regexp.Regexp{}}
for tenant, metricsRegexp := range params.SourceTenants {
compiledRegexp, err := regexp.Compile("^" + metricsRegexp + "$")
validator := hasSourceTenantsForMetrics{sourceTenants: map[string]tenantMetrics{}}
for tenant, metrics := range params.SourceTenants {
compiledRegexp, err := regexp.Compile("^" + metrics.Regexp + "$")
if err != nil {
return nil, fmt.Errorf("invalid metric name regexp: %s", metricsRegexp)
return nil, fmt.Errorf("invalid metric name regexp: %s", metrics.Regexp)
}
validator.sourceTenants[tenant] = tenantMetrics{
regexp: compiledRegexp,
description: metrics.Description,
}
validator.sourceTenants[tenant] = compiledRegexp
}
return &validator, nil
}

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

type hasSourceTenantsForMetrics struct {
sourceTenants map[string]*regexp.Regexp
sourceTenants map[string]tenantMetrics
}

func (h hasSourceTenantsForMetrics) String() string {
tenantStrings := []string{}
for tenant, metricsRegexp := range h.sourceTenants {
tenantStrings = append(tenantStrings, fmt.Sprintf("`%s`:`%s`", tenant, metricsRegexp.String()))
tenantStrings = append(tenantStrings, fmt.Sprintf("`%s`: `%s` (%s)", tenant, metricsRegexp.regexp.String(), metricsRegexp.description))
}
return fmt.Sprintf("rule group, the rule belongs to, has the required `source_tenants` configured, according to the mapping of metric names to tenants: %s", strings.Join(tenantStrings, ", "))
return fmt.Sprintf("rule group, the rule belongs to, has the required `source_tenants` configured, according to the mapping of metric names to tenants: \n %s", strings.Join(tenantStrings, "\n "))
}

func (h hasSourceTenantsForMetrics) Validate(group unmarshaler.RuleGroup, rule rulefmt.Rule, _ *prometheus.Client) []error {
Expand All @@ -54,7 +67,7 @@ func (h hasSourceTenantsForMetrics) Validate(group unmarshaler.RuleGroup, rule r
}
for _, usedMetric := range usedMetrics {
for tenant, metricsRegexp := range h.sourceTenants {
if metricsRegexp.MatchString(usedMetric.Name) && !slices.Contains(group.SourceTenants, tenant) {
if metricsRegexp.regexp.MatchString(usedMetric.Name) && !slices.Contains(group.SourceTenants, tenant) {
errs = append(errs, fmt.Errorf("rule uses metric `%s` of the tenant `%s` tenant, you should set the tenant in the groups source_tenants settings", tenant, usedMetric.Name))
}
}
Expand Down
10 changes: 5 additions & 5 deletions pkg/validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,11 +196,11 @@ var testCases = []struct {
{name: "regexpIsFullyAnchored", validator: expressionDoesNotUseMetrics{metricNameRegexps: []*regexp.Regexp{regexp.MustCompile(`^foo_bar$`)}}, rule: rulefmt.Rule{Expr: `foo_baz_baz{foo="bar"} and foo_bar`}, expectedErrors: 1},

// hasSourceTenantsForMetrics
{name: "emptyMapping", validator: hasSourceTenantsForMetrics{sourceTenants: map[string]*regexp.Regexp{}}, rule: rulefmt.Rule{Expr: `up{foo="bar"}`}, expectedErrors: 0},
{name: "usesMetricWithSourceTenantAndGroupHasSourceTenant", validator: hasSourceTenantsForMetrics{sourceTenants: map[string]*regexp.Regexp{"tenant1": regexp.MustCompile(`^teanant1_metric$`)}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant1"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 0},
{name: "usesMetricWithSourceTenantAndGroupDoesNotHaveSourceTenant", validator: hasSourceTenantsForMetrics{sourceTenants: map[string]*regexp.Regexp{"tenant1": regexp.MustCompile(`^teanant1_metric$`)}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant2"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 1},
{name: "usesMetricWithSourceTenantAndGroupHasMultipleSourceTenants", validator: hasSourceTenantsForMetrics{sourceTenants: map[string]*regexp.Regexp{"tenant1": regexp.MustCompile(`^teanant1_metric$`)}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant2", "tenant1"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 0},
{name: "usesMetricWithSourceTenantAndGroupHasMultipleSourceTenantsAndOneIsMissing", validator: hasSourceTenantsForMetrics{sourceTenants: map[string]*regexp.Regexp{"tenant1": regexp.MustCompile(`^teanant1_metric$`)}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant2", "tenant3"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 1},
{name: "emptyMapping", validator: hasSourceTenantsForMetrics{sourceTenants: map[string]tenantMetrics{}}, rule: rulefmt.Rule{Expr: `up{foo="bar"}`}, expectedErrors: 0},
{name: "usesMetricWithSourceTenantAndGroupHasSourceTenant", validator: hasSourceTenantsForMetrics{sourceTenants: map[string]tenantMetrics{"tenant1": {regexp: regexp.MustCompile(`^teanant1_metric$`)}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant1"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 0},
{name: "usesMetricWithSourceTenantAndGroupDoesNotHaveSourceTenant", validator: hasSourceTenantsForMetrics{sourceTenants: map[string]tenantMetrics{"tenant1": {regexp: regexp.MustCompile(`^teanant1_metric$`)}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant2"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 1},
{name: "usesMetricWithSourceTenantAndGroupHasMultipleSourceTenants", validator: hasSourceTenantsForMetrics{sourceTenants: map[string]tenantMetrics{"tenant1": {regexp: regexp.MustCompile(`^teanant1_metric$`)}}}, group: unmarshaler.RuleGroup{SourceTenants: []string{"tenant2", "tenant1"}}, rule: rulefmt.Rule{Expr: `teanant1_metric{foo="bar"}`}, expectedErrors: 0},
{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},

// 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 cb50345

Please sign in to comment.