Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ignoreTemplatedValues param to the ignoreTemplatedValues validator #49

Merged
merged 1 commit into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading