Skip to content

Commit

Permalink
feat: add new validator expressionIsValidPromQL
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 Jul 12, 2024
1 parent 9af9c56 commit a9d8464
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed: :warning: Unmarshalling of the rule files is strict again, this behavior was unintentionally brought when adding support for yaml comments.
- Added: support for alert field `keep_firing_for`
- Added: support for the `query_offset` field in the rule group
- Added: new validator `expressionIsValidPromQL` to check if the expression is a valid PromQL query

## [2.14.1]
- Fixed: error message in the `hasSourceTenantsForMetrics` validator
Expand Down
1 change: 1 addition & 0 deletions docs/default_validation.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ validationRules:
- name: check-all-rules
scope: All rules
validations:
- type: expressionIsValidPromQL
- type: nonEmptyLabels
- type: expressionDoesNotUseIrate
- type: validFunctionsOnCounters
Expand Down
5 changes: 5 additions & 0 deletions docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ All the supported validations are listed here. The validations are grouped by th
- [`nonEmptyLabels`](#nonemptylabels)
- [`exclusiveLabels`](#exclusivelabels)
- [PromQL expression validators](#promql-expression-validators)
- [`expressionIsValidPromQL`](#expressionisvalidpromql)
- [`expressionDoesNotUseMetrics`](#expressiondoesnotusemetrics)
- [`expressionDoesNotUseLabels`](#expressiondoesnotuselabels)
- [`expressionDoesNotUseOlderDataThan`](#expressiondoesnotuseolderdatathan)
Expand Down Expand Up @@ -205,6 +206,10 @@ params:
```
### PromQL expression validators

#### `expressionIsValidPromQL`

Fails if the expression is not a valid PromQL query.

#### `expressionDoesNotUseMetrics`

Fails if the rule expression uses metrics matching any of the metric name fully anchored(will be surrounded by `^...$`) regexps.
Expand Down
1 change: 1 addition & 0 deletions pkg/validator/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var registeredUniversalRuleValidators = map[string]validatorCreator{
"nonEmptyLabels": newNonEmptyLabels,
"exclusiveLabels": newExclusiveLabels,

"expressionIsValidPromQL": newExpressionIsValidPromQL,
"validFunctionsOnCounters": newValidFunctionsOnCounters,
"rateBeforeAggregation": newRateBeforeAggregation,
"expressionDoesNotUseLabels": newExpressionDoesNotUseLabels,
Expand Down
17 changes: 17 additions & 0 deletions pkg/validator/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,23 @@ import (
"gopkg.in/yaml.v3"
)

func newExpressionIsValidPromQL(_ yaml.Node) (Validator, error) {
return &expressionIsValidPromQL{}, nil
}

type expressionIsValidPromQL struct{}

func (h expressionIsValidPromQL) String() string {
return "expression is a valid PromQL query"
}

func (h expressionIsValidPromQL) Validate(_ unmarshaler.RuleGroup, rule rulefmt.Rule, _ *prometheus.Client) []error {
if _, err := parser.ParseExpr(rule.Expr); err != nil {
return []error{fmt.Errorf("expression %s is not a valid PromQL query: %w", rule.Expr, err)}
}
return []error{}
}

func newExpressionDoesNotUseOlderDataThan(paramsConfig yaml.Node) (Validator, error) {
params := struct {
Limit model.Duration `yaml:"limit"`
Expand Down
4 changes: 4 additions & 0 deletions pkg/validator/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ var testCases = []struct {
// keepFiringForIsNotLongerThan
{name: "keepFiringForIsNotLongerThanOK", validator: keepFiringForIsNotLongerThan{limit: model.Duration(time.Minute)}, rule: rulefmt.Rule{KeepFiringFor: model.Duration(time.Second)}, expectedErrors: 0},
{name: "keepFiringForIsNotLongerThanWrong", validator: keepFiringForIsNotLongerThan{limit: model.Duration(time.Minute)}, rule: rulefmt.Rule{KeepFiringFor: model.Duration(time.Minute * 2)}, expectedErrors: 1},

// expressionIsValidPromQL
{name: "expressionIsValidPromQL_OK", validator: expressionIsValidPromQL{}, rule: rulefmt.Rule{Expr: "sum(rate(foo{bar='baz'}[1m]))"}, expectedErrors: 0},
{name: "expressionIsValidPromQL_Invalid", validator: expressionIsValidPromQL{}, rule: rulefmt.Rule{Expr: "sum(rate(foo{bar='baz'} | ??? [1m]))"}, expectedErrors: 1},
}

func Test(t *testing.T) {
Expand Down

0 comments on commit a9d8464

Please sign in to comment.