Skip to content

Commit

Permalink
feat: add ignoreTemplatedValues param to the ignoreTemplatedValues va…
Browse files Browse the repository at this point in the history
…lidator (#49)

Signed-off-by: Martin Chodur <[email protected]>
  • Loading branch information
FUSAKLA authored Feb 28, 2024
1 parent e6bd372 commit e09713a
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
- Added new param `ignoreTemplatedValues` to the `labelHasAllowedValue` validator to ignore templated values in the label.
- Added new validation rule scope `Group` to validate the rule group itself (not the rules in it).
- Added new `Group` scope validator `hasAllowedEvaluationInterval` to check if the rule group has the `interval` in the configured range and possibility to enforce it to be configured.
- CHANGED: The validator `allowedSourceTenants` is now allowed only in the `Group` scope validation rules.
Expand Down
4 changes: 4 additions & 0 deletions docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ params:

Fails if rule label value is not one of the allowed values. If the `commaSeparatedValue` is set to true, the label value
to true, the label value is split by a comma, and the distinct values are checked if valid.
Since the labels can be templated, but Promruval cannot tell if the resulting value will be valid,
there is the `ignoreTemplatedValues` option, that allows you to ignore the templated values.

> It's quite common to have well known severities for alerts which can be important even in the
> Alertmanager routing tree. Ths is how you can make sure only the well-known severities are used.

Expand All @@ -97,6 +100,7 @@ params:
label: "foo"
allowedValues: [ "foo", "bar" ]
commaSeparatedValue: true
ignoreTemplatedValues: false
```

### `nonEmptyLabels`
Expand Down
2 changes: 1 addition & 1 deletion examples/human_readable.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ <h2><a href="#check-severity-label">check-severity-label</a></h2>
<h2><a href="#check-team-label">check-team-label</a></h2>
<ul>
<li>Alert has labels: <code>xxx</code></li>
<li>Alert label <code>team</code> has one of the allowed values: <code>[email protected]</code></li>
<li>Alert label <code>team</code> has one of the allowed values: <code>[email protected]</code> (templated values are ignored)</li>
</ul>

<h2><a href="#check-playbook-annotation">check-playbook-annotation</a></h2>
Expand Down
2 changes: 1 addition & 1 deletion examples/human_readable.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Validation rules:

check-team-label
- Alert has labels: `xxx`
- Alert label `team` has one of the allowed values: `[email protected]`
- Alert label `team` has one of the allowed values: `[email protected]` (templated values are ignored)

check-playbook-annotation
- Alert has any of these annotations: `playbook`,`link`
Expand Down
13 changes: 13 additions & 0 deletions examples/rules/rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,19 @@ groups:
# ignore_validations: expressionSelectorsMatchesAnything, hasLabels
}
for: 1m
labels:
team: "{{ .Labels.team }}"
annotations:
title: test alert
playbook: http://foo.bar/nonexisting/playbook

- alert: test2
expr: up == 0
for: 1m
labels:
team: "{{ .Labels.team }}"
xxx: fooo
severity: critical
annotations:
title: test alert
playbook: http://foo.bar/nonexisting/playbook
1 change: 1 addition & 0 deletions examples/validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ validationRules:
label: "team"
allowedValues:
- "[email protected]"
ignoreTemplatedValues: true

- name: check-playbook-annotation
scope: Alert
Expand Down
25 changes: 17 additions & 8 deletions pkg/validator/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,10 @@ func (h hasAnyOfLabels) Validate(_ unmarshaler.RuleGroup, rule rulefmt.Rule, _ *

func newLabelHasAllowedValue(paramsConfig yaml.Node) (Validator, error) {
params := struct {
Label string `yaml:"label"`
AllowedValues []string `yaml:"allowedValues"`
CommaSeparatedValue bool `yaml:"commaSeparatedValue"`
Label string `yaml:"label"`
AllowedValues []string `yaml:"allowedValues"`
CommaSeparatedValue bool `yaml:"commaSeparatedValue"`
IgnoreTemplatedValues bool `yaml:"ignoreTemplatedValues"`
}{}
if err := paramsConfig.Decode(&params); err != nil {
return nil, err
Expand All @@ -156,28 +157,36 @@ func newLabelHasAllowedValue(paramsConfig yaml.Node) (Validator, error) {
if len(params.AllowedValues) == 0 {
return nil, fmt.Errorf("missing allowedValues")
}
return &labelHasAllowedValue{label: params.Label, allowedValues: params.AllowedValues, commaSeparatedValue: params.CommaSeparatedValue}, nil
return &labelHasAllowedValue{label: params.Label, allowedValues: params.AllowedValues, commaSeparatedValue: params.CommaSeparatedValue, ignoreTemplatedValues: params.IgnoreTemplatedValues}, nil
}

type labelHasAllowedValue struct {
label string
allowedValues []string
commaSeparatedValue bool
label string
allowedValues []string
commaSeparatedValue bool
ignoreTemplatedValues bool
}

func (h labelHasAllowedValue) String() string {
text := fmt.Sprintf("has one of the allowed values: `%s`", strings.Join(h.allowedValues, "`,`"))
if h.commaSeparatedValue {
text = "split by comma " + text
}
return fmt.Sprintf("label `%s` %s", h.label, text)
text = fmt.Sprintf("label `%s` %s", h.label, text)
if h.ignoreTemplatedValues {
text += " (templated values are ignored)"
}
return text
}

func (h labelHasAllowedValue) Validate(_ unmarshaler.RuleGroup, rule rulefmt.Rule, _ *prometheus.Client) []error {
ruleValue, ok := rule.Labels[h.label]
if !ok {
return []error{}
}
if h.ignoreTemplatedValues && strings.Contains(ruleValue, "{{") {
return []error{}
}
valuesToCheck := []string{ruleValue}
if h.commaSeparatedValue {
valuesToCheck = strings.Split(ruleValue, ",")
Expand Down
2 changes: 2 additions & 0 deletions pkg/validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ var testCases = []struct {
{name: "ruleHasCsvLabelWithAllowedValue", validator: labelHasAllowedValue{label: "foo", allowedValues: []string{"bar"}, commaSeparatedValue: true}, rule: rulefmt.Rule{Labels: map[string]string{"foo": "xxx,bar"}}, expectedErrors: 0},
{name: "ruleDoesNotHaveLabelWithAllowedValue", validator: labelHasAllowedValue{label: "foo", allowedValues: []string{"bar"}}, rule: rulefmt.Rule{Labels: map[string]string{"foo": "xxx"}}, expectedErrors: 1},
{name: "ruleHasCsvLabelWithoutAllowedValue", validator: labelHasAllowedValue{label: "foo", allowedValues: []string{"bar"}, commaSeparatedValue: true}, rule: rulefmt.Rule{Labels: map[string]string{"foo": "xxx,yyy"}}, expectedErrors: 1},
{name: "ruleHasTemplatedLabelAndCannotHave", validator: labelHasAllowedValue{label: "foo", allowedValues: []string{"bar"}, ignoreTemplatedValues: false}, rule: rulefmt.Rule{Labels: map[string]string{"foo": "{{ .Labels.foo }}"}}, expectedErrors: 1},
{name: "ruleHasTemplatedLabelAndCannotHave", validator: labelHasAllowedValue{label: "foo", allowedValues: []string{"bar"}, ignoreTemplatedValues: true}, rule: rulefmt.Rule{Labels: map[string]string{"foo": "{{ .Labels.foo }}"}}, expectedErrors: 0},

// annotationHasAllowedValue
{name: "ruleHasAnnotationWithAllowedValue", validator: annotationHasAllowedValue{annotation: "foo", allowedValues: []string{"bar"}}, rule: rulefmt.Rule{Annotations: map[string]string{"foo": "bar"}}, expectedErrors: 0},
Expand Down

0 comments on commit e09713a

Please sign in to comment.