Rules API: Allow filtering by rule name

Introduces support for a new query parameter in the `/rules` API endpoint that allows filtering by rule names.

If all the rules of a group are filtered, we skip the group entirely.

Signed-off-by: gotjosh <josue.abreu@gmail.com>
This commit is contained in:
gotjosh 2023-04-18 10:07:32 +01:00
parent 7309ac2721
commit f3394bf7a1
No known key found for this signature in database
GPG key ID: A6E1DDE38FF3C74E
3 changed files with 56 additions and 6 deletions

View file

@ -673,7 +673,9 @@ GET /api/v1/rules
``` ```
URL query parameters: URL query parameters:
- `type=alert|record`: return only the alerting rules (e.g. `type=alert`) or the recording rules (e.g. `type=record`). When the parameter is absent or empty, no filtering is done. - `type=alert|record`: return only the alerting rules (e.g. `type=alert`) or the recording rules (e.g. `type=record`). When the parameter is absent or empty, no filtering is done.
- `rules=alertName,RuleName`: return only the alerting and recording rules with the specified names. If we've filtered out all the rules of a group, the group is not returned. When the parameter is absent or empty, no filtering is done.
```json ```json
$ curl http://localhost:9090/api/v1/rules $ curl http://localhost:9090/api/v1/rules

View file

@ -1296,6 +1296,16 @@ func (api *API) rules(r *http.Request) apiFuncResult {
res := &RuleDiscovery{RuleGroups: make([]*RuleGroup, len(ruleGroups))} res := &RuleDiscovery{RuleGroups: make([]*RuleGroup, len(ruleGroups))}
typ := strings.ToLower(r.URL.Query().Get("type")) typ := strings.ToLower(r.URL.Query().Get("type"))
// Parse the rule names into a comma separated list of rule names, then create a set.
rulesQuery := r.URL.Query().Get("rules")
ruleNamesSet := map[string]struct{}{}
if rulesQuery != "" {
names := strings.Split(rulesQuery, ",")
for _, rn := range names {
ruleNamesSet[strings.TrimSpace(rn)] = struct{}{}
}
}
if typ != "" && typ != "alert" && typ != "record" { if typ != "" && typ != "alert" && typ != "record" {
return invalidParamError(errors.Errorf("not supported value %q", typ), "type") return invalidParamError(errors.Errorf("not supported value %q", typ), "type")
} }
@ -1313,14 +1323,20 @@ func (api *API) rules(r *http.Request) apiFuncResult {
EvaluationTime: grp.GetEvaluationTime().Seconds(), EvaluationTime: grp.GetEvaluationTime().Seconds(),
LastEvaluation: grp.GetLastEvaluation(), LastEvaluation: grp.GetLastEvaluation(),
} }
for _, r := range grp.Rules() { for _, rr := range grp.Rules() {
var enrichedRule Rule var enrichedRule Rule
lastError := "" if len(ruleNamesSet) > 0 {
if r.LastError() != nil { if _, ok := ruleNamesSet[rr.Name()]; !ok {
lastError = r.LastError().Error() continue
}
} }
switch rule := r.(type) {
lastError := ""
if rr.LastError() != nil {
lastError = rr.LastError().Error()
}
switch rule := rr.(type) {
case *rules.AlertingRule: case *rules.AlertingRule:
if !returnAlerts { if !returnAlerts {
break break
@ -1358,11 +1374,16 @@ func (api *API) rules(r *http.Request) apiFuncResult {
err := errors.Errorf("failed to assert type of rule '%v'", rule.Name()) err := errors.Errorf("failed to assert type of rule '%v'", rule.Name())
return apiFuncResult{nil, &apiError{errorInternal, err}, nil, nil} return apiFuncResult{nil, &apiError{errorInternal, err}, nil, nil}
} }
if enrichedRule != nil { if enrichedRule != nil {
apiRuleGroup.Rules = append(apiRuleGroup.Rules, enrichedRule) apiRuleGroup.Rules = append(apiRuleGroup.Rules, enrichedRule)
} }
} }
res.RuleGroups[i] = apiRuleGroup
// If the rule group response has no rules, skip it - this means we filtered all the rules of this group.
if len(apiRuleGroup.Rules) > 0 {
res.RuleGroups[i] = apiRuleGroup
}
} }
return apiFuncResult{res, nil, nil, nil} return apiFuncResult{res, nil, nil, nil}
} }

View file

@ -1973,6 +1973,33 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, es storage.E
}, },
}, },
}, },
{
endpoint: api.rules,
query: url.Values{"rules": []string{"test_metric4"}},
response: &RuleDiscovery{
RuleGroups: []*RuleGroup{
{
Name: "grp",
File: "/path/to/file",
Interval: 1,
Limit: 0,
Rules: []Rule{
AlertingRule{
State: "inactive",
Name: "test_metric4",
Query: "up == 1",
Duration: 1,
Labels: labels.Labels{},
Annotations: labels.Labels{},
Alerts: []*Alert{},
Health: "unknown",
Type: "alerting",
},
},
},
},
},
},
{ {
endpoint: api.queryExemplars, endpoint: api.queryExemplars,
query: url.Values{ query: url.Values{