Skip to content

Commit 527d25f

Browse files
authored
Add matchers filtering field to rules api (#6083)
* Add matchers filtering field to rules api Signed-off-by: Eunice Kim <[email protected]> * Address comments Signed-off-by: Eunice Kim <[email protected]> * Address comments Signed-off-by: Eunice Kim <[email protected]> --------- Signed-off-by: Eunice Kim <[email protected]>
1 parent 1c8ebe3 commit 527d25f

File tree

6 files changed

+321
-51
lines changed

6 files changed

+321
-51
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@
2222
* [ENHANCEMENT] Upgrade go to 1.22.5 #6014 #6072
2323
* [ENHANCEMENT] Ingester: Add a new experimental `-ingester.labels-string-interning-enabled` flag to enable string interning for metrics labels. #6057
2424
* [ENHANCEMENT] Ingester: Add link to renew 10% of the ingesters tokens in the admin page. #6063
25+
* [ENHANCEMENT] Ruler: Add support for filtering by `state` and `health` field on Rules API. #6040
26+
* [ENHANCEMENT] Ruler: Add support for filtering by `match` field on Rules API. #6083
2527
* [BUGFIX] Configsdb: Fix endline issue in db password. #5920
2628
* [BUGFIX] Ingester: Fix `user` and `type` labels for the `cortex_ingester_tsdb_head_samples_appended_total` TSDB metric. #5952
2729
* [BUGFIX] Querier: Enforce max query length check for `/api/v1/series` API even though `ignoreMaxQueryLength` is set to true. #6018
2830
* [BUGFIX] Ingester: Fix issue with the minimize token generator where it was not taking in consideration the current ownerhip of an instance when generating extra tokens. #6062
29-
* [ENHANCEMENT] Ruler: Add support for filtering by `state` and `health` field on Rules API. #6040
3031

3132
## 1.17.1 2024-05-20
3233

pkg/ruler/api.go

+8
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,21 @@ func (a *API) PrometheusRules(w http.ResponseWriter, req *http.Request) {
147147
return
148148
}
149149

150+
_, err = parseMatchersParam(req.Form["match[]"])
151+
if err != nil {
152+
level.Error(logger).Log("msg", "error parsing match query params", "err", err)
153+
util_api.RespondError(logger, w, v1.ErrBadData, fmt.Sprintf("error parsing match params %s", err), http.StatusBadRequest)
154+
return
155+
}
156+
150157
rulesRequest := RulesRequest{
151158
RuleNames: req.Form["rule_name[]"],
152159
RuleGroupNames: req.Form["rule_group[]"],
153160
Files: req.Form["file[]"],
154161
Type: typ,
155162
State: state,
156163
Health: health,
164+
Matchers: req.Form["match[]"],
157165
}
158166

159167
w.Header().Set("Content-Type", "application/json")

pkg/ruler/ruler.go

+58
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,10 @@ func (r *Ruler) getLocalRules(userID string, rulesRequest RulesRequest, includeB
884884
ruleType := rulesRequest.Type
885885
alertState := rulesRequest.State
886886
health := rulesRequest.Health
887+
matcherSets, err := parseMatchersParam(rulesRequest.Matchers)
888+
if err != nil {
889+
return nil, errors.Wrap(err, "error parsing matcher values")
890+
}
887891

888892
returnAlerts := ruleType == "" || ruleType == alertingRuleFilter
889893
returnRecording := (ruleType == "" || ruleType == recordingRuleFilter) && alertState == ""
@@ -928,6 +932,9 @@ func (r *Ruler) getLocalRules(userID string, rulesRequest RulesRequest, includeB
928932
if !returnByHealth(health, string(r.Health())) {
929933
continue
930934
}
935+
if !matchesMatcherSets(matcherSets, r.Labels()) {
936+
continue
937+
}
931938
lastError := ""
932939
if r.LastError() != nil {
933940
lastError = r.LastError().Error()
@@ -1009,6 +1016,7 @@ func (r *Ruler) getLocalRules(userID string, rulesRequest RulesRequest, includeB
10091016
fileSet,
10101017
returnAlerts,
10111018
returnRecording,
1019+
matcherSets,
10121020
})
10131021
if err != nil {
10141022
return nil, err
@@ -1023,6 +1031,7 @@ type groupListFilter struct {
10231031
fileSet map[string]struct{}
10241032
returnAlerts bool
10251033
returnRecording bool
1034+
matcherSets [][]*labels.Matcher
10261035
}
10271036

10281037
// ruleGroupListToGroupStateDesc converts rulespb.RuleGroupList to []*GroupStateDesc while accepting filters to control what goes to the
@@ -1068,6 +1077,9 @@ func (r *Ruler) ruleGroupListToGroupStateDesc(userID string, backupGroups rulesp
10681077
continue
10691078
}
10701079
}
1080+
if !matchesMatcherSets(filters.matcherSets, cortexpb.FromLabelAdaptersToLabels(r.Labels)) {
1081+
continue
1082+
}
10711083

10721084
var ruleDesc *RuleStateDesc
10731085
query, err := parser.ParseExpr(r.GetExpr())
@@ -1162,6 +1174,7 @@ func (r *Ruler) getShardedRules(ctx context.Context, userID string, rulesRequest
11621174
RuleGroupNames: rulesRequest.GetRuleGroupNames(),
11631175
Files: rulesRequest.GetFiles(),
11641176
Type: rulesRequest.GetType(),
1177+
Matchers: rulesRequest.GetMatchers(),
11651178
})
11661179

11671180
if err != nil {
@@ -1320,3 +1333,48 @@ func returnByState(requestState string, alertState string) bool {
13201333
func returnByHealth(requestHealth string, ruleHealth string) bool {
13211334
return requestHealth == "" || requestHealth == ruleHealth
13221335
}
1336+
1337+
func parseMatchersParam(matchers []string) ([][]*labels.Matcher, error) {
1338+
var matcherSets [][]*labels.Matcher
1339+
for _, s := range matchers {
1340+
matchers, err := parser.ParseMetricSelector(s)
1341+
if err != nil {
1342+
return nil, err
1343+
}
1344+
matcherSets = append(matcherSets, matchers)
1345+
}
1346+
1347+
OUTER:
1348+
for _, ms := range matcherSets {
1349+
for _, lm := range ms {
1350+
if lm != nil && !lm.Matches("") {
1351+
continue OUTER
1352+
}
1353+
}
1354+
return nil, errors.New("match[] must contain at least one non-empty matcher")
1355+
}
1356+
return matcherSets, nil
1357+
}
1358+
1359+
func matches(l labels.Labels, matchers ...*labels.Matcher) bool {
1360+
for _, m := range matchers {
1361+
if v := l.Get(m.Name); !m.Matches(v) {
1362+
return false
1363+
}
1364+
}
1365+
return true
1366+
}
1367+
1368+
// matchesMatcherSets ensures all matches in each matcher set are ANDed and the set of those is ORed.
1369+
func matchesMatcherSets(matcherSets [][]*labels.Matcher, l labels.Labels) bool {
1370+
if len(matcherSets) == 0 {
1371+
return true
1372+
}
1373+
1374+
for _, matchers := range matcherSets {
1375+
if matches(l, matchers...) {
1376+
return true
1377+
}
1378+
}
1379+
return false
1380+
}

pkg/ruler/ruler.pb.go

+116-50
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ruler/ruler.proto

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ message RulesRequest {
2626
string type = 4;
2727
string state = 5;
2828
string health = 6;
29+
repeated string matchers = 7;
2930
}
3031

3132
message RulesResponse {

0 commit comments

Comments
 (0)