Skip to content

Commit

Permalink
feat: add new config option additionalDetails to all validators
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Chodur <[email protected]>
  • Loading branch information
FUSAKLA committed Mar 4, 2024
1 parent 06414e0 commit 3ce2f85
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 24 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]
- Fixed error messages for the `hasSourceTenantsForMetrics` and `expressionDoesNotUseIrate` validators.
- Added new config option `additionalDetails` to all validators providing possibility to add custom details about the error and how to solve it.
Those will be appended to the validator error message in a parenthesis if provided.
Example configuration:
```yaml
- name: expressionDoesNotUseIrate
additionalDetails: "Just do as I say!"
```
Example output:
```yaml
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!)
```
## [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.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ validationRules:
validations:
# Name of the validation type. See the /docs/validations.md.
- type: hasLabels
# Additional detaild that will be appended to the default error message. Useful to customize the error message.
additionalDetails: "We do this because ..."
# Parameters of the validation.
params:
labels: [ "severity" ]
Expand Down
1 change: 1 addition & 0 deletions examples/human_readable.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ <h2><a href="#check-severity-label">check-severity-label</a></h2>
<li>Alert if rule has label <code>severity</code> with value <code>info</code> , it cannot have label <code>page</code></li>
<li>Alert expression can be successfully evaluated on the live Prometheus instance</li>
<li>Alert expression uses only labels that are actually present in Prometheus</li>
<li>Alert expression does not use irate</li>
<li>Alert expression selectors actually matches any series in Prometheus</li>
<li>Alert expression does not use data older than <code>6h0m0s</code></li>
</ul>
Expand Down
1 change: 1 addition & 0 deletions examples/human_readable.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Validation rules:
- Alert if rule has label `severity` with value `info` , it cannot have label `page`
- Alert expression can be successfully evaluated on the live Prometheus instance
- Alert expression uses only labels that are actually present in Prometheus
- Alert expression does not use irate
- Alert expression selectors actually matches any series in Prometheus
- Alert expression does not use data older than `6h0m0s`

Expand Down
2 changes: 2 additions & 0 deletions examples/validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ validationRules:
timeSeriesLimit: 20
evaluationDurationLimit: 10s
- type: expressionUsesExistingLabels
- type: expressionDoesNotUseIrate
additionalDetails: "Just do as I say!"
- type: expressionSelectorsMatchesAnything
- type: expressionDoesNotUseOlderDataThan
params:
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ rulesIteration:
if newValidator == nil {
continue
}
newRule.AddValidator(newValidator)
newRule.AddValidator(newValidator, validatorConfig.AdditionalDetails)
}
validationRules = append(validationRules, newRule)
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ type ValidationRule struct {
}

type ValidatorConfig struct {
ValidatorType string `yaml:"type"`
Params yaml.Node `yaml:"params"`
ValidatorType string `yaml:"type"`
AdditionalDetails string `yaml:"additionalDetails"`
Params yaml.Node `yaml:"params"`
}

type ValidationScope string
Expand Down
38 changes: 22 additions & 16 deletions pkg/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"fmt"
"io"
"os"
"reflect"
"slices"
"strings"
"time"

Expand All @@ -20,6 +20,23 @@ import (
"gopkg.in/yaml.v3"
)

func validateWithDetails(v validationrule.ValidatorWithDetails, group unmarshaler.RuleGroup, rule rulefmt.Rule, prometheusClient *prometheus.Client) []error {
var reportedError error
validatorName := v.Name()
additionalDetails := v.AdditionalDetails()
validationErrors := v.Validate(group, rule, prometheusClient)
errs := make([]error, 0, len(validationErrors))
for _, err := range validationErrors {
if additionalDetails != "" {
reportedError = fmt.Errorf("%s: %w (%s)", validatorName, err, additionalDetails)
} else {
reportedError = fmt.Errorf("%s: %w", validatorName, err)
}
errs = append(errs, reportedError)
}
return errs
}

func Files(fileNames []string, validationRules []*validationrule.ValidationRule, excludeAnnotationName, disableValidationsComment string, prometheusClient *prometheus.Client) *report.ValidationReport {
validationReport := report.NewValidationReport()
for _, r := range validationRules {
Expand Down Expand Up @@ -58,10 +75,7 @@ func Files(fileNames []string, validationRules []*validationrule.ValidationRule,
continue
}
for _, v := range rule.Validators() {
validatorName := reflect.TypeOf(v).Elem().Name()
for _, err := range v.Validate(group, rulefmt.Rule{}, prometheusClient) {
groupReport.Errors = append(groupReport.Errors, fmt.Errorf("%s: %w", validatorName, err))
}
groupReport.Errors = append(groupReport.Errors, validateWithDetails(v, group, rulefmt.Rule{}, prometheusClient)...)
}
}
if len(groupReport.Errors) > 0 {
Expand Down Expand Up @@ -103,19 +117,11 @@ func Files(fileNames []string, validationRules []*validationrule.ValidationRule,
continue
}
for _, v := range rule.Validators() {
skipValidator := false
validatorName := reflect.TypeOf(v).Elem().Name()
for _, dv := range disabledValidators {
if validatorName == dv {
skipValidator = true
}
}
if skipValidator {
validatorName := v.Name()
if slices.Contains(disabledValidators, validatorName) {
continue
}
for _, err := range v.Validate(group, originalRule, prometheusClient) {
ruleReport.Errors = append(ruleReport.Errors, fmt.Errorf("%s: %w", validatorName, err))
}
ruleReport.Errors = append(ruleReport.Errors, validateWithDetails(v, group, originalRule, prometheusClient)...)
log.Debugf("validation of file %s group %s using \"%s\" took %s", fileName, group.Name, v, time.Since(start))
}
if len(ruleReport.Errors) > 0 {
Expand Down
36 changes: 31 additions & 5 deletions pkg/validationrule/validation_rule.go
Original file line number Diff line number Diff line change
@@ -1,30 +1,56 @@
package validationrule

import (
"reflect"

"github.com/fusakla/promruval/v2/pkg/config"
"github.com/fusakla/promruval/v2/pkg/validator"
)

type ValidatorWithDetails interface {
validator.Validator
AdditionalDetails() string
Name() string
}

type validatorWithAdditionalDetails struct {
validator.Validator
additionalDetails string
name string
}

func (v validatorWithAdditionalDetails) AdditionalDetails() string {
return v.additionalDetails
}

func (v validatorWithAdditionalDetails) Name() string {
return v.name
}

func New(name string, scope config.ValidationScope) *ValidationRule {
return &ValidationRule{
name: name,
scope: scope,
validators: []validator.Validator{},
validators: make([]ValidatorWithDetails, 0),
}
}

type ValidationRule struct {
name string
scope config.ValidationScope
validators []validator.Validator
validators []ValidatorWithDetails
}

func (r *ValidationRule) Validators() []validator.Validator {
func (r *ValidationRule) Validators() []ValidatorWithDetails {
return r.validators
}

func (r *ValidationRule) AddValidator(newValidator validator.Validator) {
r.validators = append(r.validators, newValidator)
func (r *ValidationRule) AddValidator(newValidator validator.Validator, additionalDetails string) {
r.validators = append(r.validators, &validatorWithAdditionalDetails{
Validator: newValidator,
additionalDetails: additionalDetails,
name: reflect.TypeOf(newValidator).Elem().Name(),
})
}

func (r *ValidationRule) Name() string {
Expand Down

0 comments on commit 3ce2f85

Please sign in to comment.