mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-24 05:04:05 -08:00
Add label-matcher support to Rules API (#10194)
* Add label-matcher support to Rules API Signed-off-by: Saswata Mukherjee <saswataminsta@yahoo.com> Signed-off-by: Yijie Qin <qinyijie@amazon.com> * Implement suggestions Signed-off-by: Saswata Mukherjee <saswataminsta@yahoo.com> Signed-off-by: Yijie Qin <qinyijie@amazon.com> * Match any matcherSet instead of all Signed-off-by: Saswata Mukherjee <saswataminsta@yahoo.com> Signed-off-by: Yijie Qin <qinyijie@amazon.com> * Don't treat labels.Labels as slice Signed-off-by: Saswata Mukherjee <saswataminsta@yahoo.com> Signed-off-by: Yijie Qin <qinyijie@amazon.com> * Remove non-templated check and fix tests Signed-off-by: Saswata Mukherjee <saswataminsta@yahoo.com> Signed-off-by: Yijie Qin <qinyijie@amazon.com> * Update docs Signed-off-by: Saswata Mukherjee <saswataminsta@yahoo.com> Signed-off-by: Yijie Qin <qinyijie@amazon.com> * fix comments Signed-off-by: Yijie Qin <qinyijie@amazon.com> * fix comment Signed-off-by: Yijie Qin <qinyijie@amazon.com> * Add comment for matching logic, fix tests after rebase Signed-off-by: Saswata Mukherjee <saswataminsta@yahoo.com> --------- Signed-off-by: Saswata Mukherjee <saswataminsta@yahoo.com> Signed-off-by: Yijie Qin <qinyijie@amazon.com> Co-authored-by: Yijie Qin <qinyijie@amazon.com>
This commit is contained in:
parent
b1c106a9ce
commit
398f42de5f
|
@ -693,7 +693,8 @@ URL query parameters:
|
|||
- `rule_name[]=<string>`: only return rules with the given rule name. If the parameter is repeated, rules with any of the provided names are returned. 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.
|
||||
- `rule_group[]=<string>`: only return rules with the given rule group name. If the parameter is repeated, rules with any of the provided rule group names are returned. When the parameter is absent or empty, no filtering is done.
|
||||
- `file[]=<string>`: only return rules with the given filepath. If the parameter is repeated, rules with any of the provided filepaths are returned. When the parameter is absent or empty, no filtering is done.
|
||||
- `exclude_alerts=<bool>`: only return rules, do not return active alerts.
|
||||
- `exclude_alerts=<bool>`: only return rules, do not return active alerts.
|
||||
- `match[]=<label_selector>`: only return rules that have configured labels that satisfy the label selectors. If the parameter is repeated, rules that match any of the sets of label selectors are returned. Note that matching is on the labels in the definition of each rule, not on the values after template expansion (for alerting rules). Optional.
|
||||
|
||||
```json
|
||||
$ curl http://localhost:9090/api/v1/rules
|
||||
|
|
|
@ -151,7 +151,42 @@ func (g *Group) Name() string { return g.name }
|
|||
func (g *Group) File() string { return g.file }
|
||||
|
||||
// Rules returns the group's rules.
|
||||
func (g *Group) Rules() []Rule { return g.rules }
|
||||
func (g *Group) Rules(matcherSets ...[]*labels.Matcher) []Rule {
|
||||
if len(matcherSets) == 0 {
|
||||
return g.rules
|
||||
}
|
||||
var rules []Rule
|
||||
for _, rule := range g.rules {
|
||||
if matchesMatcherSets(matcherSets, rule.Labels()) {
|
||||
rules = append(rules, rule)
|
||||
}
|
||||
}
|
||||
return rules
|
||||
}
|
||||
|
||||
func matches(lbls labels.Labels, matchers ...*labels.Matcher) bool {
|
||||
for _, m := range matchers {
|
||||
if v := lbls.Get(m.Name); !m.Matches(v) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// matchesMatcherSets ensures all matches in each matcher set are ANDed and the set of those is ORed.
|
||||
func matchesMatcherSets(matcherSets [][]*labels.Matcher, lbls labels.Labels) bool {
|
||||
if len(matcherSets) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
var ok bool
|
||||
for _, matchers := range matcherSets {
|
||||
if matches(lbls, matchers...) {
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
// Queryable returns the group's querable.
|
||||
func (g *Group) Queryable() storage.Queryable { return g.opts.Queryable }
|
||||
|
|
|
@ -380,13 +380,13 @@ func (m *Manager) RuleGroups() []*Group {
|
|||
}
|
||||
|
||||
// Rules returns the list of the manager's rules.
|
||||
func (m *Manager) Rules() []Rule {
|
||||
func (m *Manager) Rules(matcherSets ...[]*labels.Matcher) []Rule {
|
||||
m.mtx.RLock()
|
||||
defer m.mtx.RUnlock()
|
||||
|
||||
var rules []Rule
|
||||
for _, g := range m.groups {
|
||||
rules = append(rules, g.rules...)
|
||||
rules = append(rules, g.Rules(matcherSets...)...)
|
||||
}
|
||||
|
||||
return rules
|
||||
|
|
|
@ -1397,6 +1397,11 @@ func (api *API) rules(r *http.Request) apiFuncResult {
|
|||
rgSet := queryFormToSet(r.Form["rule_group[]"])
|
||||
fSet := queryFormToSet(r.Form["file[]"])
|
||||
|
||||
matcherSets, err := parseMatchersParam(r.Form["match[]"])
|
||||
if err != nil {
|
||||
return apiFuncResult{nil, &apiError{errorBadData, err}, nil, nil}
|
||||
}
|
||||
|
||||
ruleGroups := api.rulesRetriever(r.Context()).RuleGroups()
|
||||
res := &RuleDiscovery{RuleGroups: make([]*RuleGroup, 0, len(ruleGroups))}
|
||||
typ := strings.ToLower(r.URL.Query().Get("type"))
|
||||
|
@ -1436,7 +1441,8 @@ func (api *API) rules(r *http.Request) apiFuncResult {
|
|||
EvaluationTime: grp.GetEvaluationTime().Seconds(),
|
||||
LastEvaluation: grp.GetLastEvaluation(),
|
||||
}
|
||||
for _, rr := range grp.Rules() {
|
||||
|
||||
for _, rr := range grp.Rules(matcherSets...) {
|
||||
var enrichedRule Rule
|
||||
|
||||
if len(rnSet) > 0 {
|
||||
|
|
|
@ -261,11 +261,36 @@ func (m *rulesRetrieverMock) CreateAlertingRules() {
|
|||
false,
|
||||
log.NewNopLogger(),
|
||||
)
|
||||
|
||||
rule4 := rules.NewAlertingRule(
|
||||
"test_metric6",
|
||||
expr2,
|
||||
time.Second,
|
||||
0,
|
||||
labels.FromStrings("testlabel", "rule"),
|
||||
labels.Labels{},
|
||||
labels.Labels{},
|
||||
"",
|
||||
true,
|
||||
log.NewNopLogger(),
|
||||
)
|
||||
rule5 := rules.NewAlertingRule(
|
||||
"test_metric7",
|
||||
expr2,
|
||||
time.Second,
|
||||
0,
|
||||
labels.FromStrings("templatedlabel", "{{ $externalURL }}"),
|
||||
labels.Labels{},
|
||||
labels.Labels{},
|
||||
"",
|
||||
true,
|
||||
log.NewNopLogger(),
|
||||
)
|
||||
var r []*rules.AlertingRule
|
||||
r = append(r, rule1)
|
||||
r = append(r, rule2)
|
||||
r = append(r, rule3)
|
||||
r = append(r, rule4)
|
||||
r = append(r, rule5)
|
||||
m.alertingRules = r
|
||||
}
|
||||
|
||||
|
@ -300,7 +325,9 @@ func (m *rulesRetrieverMock) CreateRuleGroups() {
|
|||
recordingExpr, err := parser.ParseExpr(`vector(1)`)
|
||||
require.NoError(m.testing, err, "unable to parse alert expression")
|
||||
recordingRule := rules.NewRecordingRule("recording-rule-1", recordingExpr, labels.Labels{})
|
||||
recordingRule2 := rules.NewRecordingRule("recording-rule-2", recordingExpr, labels.FromStrings("testlabel", "rule"))
|
||||
r = append(r, recordingRule)
|
||||
r = append(r, recordingRule2)
|
||||
|
||||
group := rules.NewGroup(rules.GroupOptions{
|
||||
Name: "grp",
|
||||
|
@ -2151,6 +2178,28 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, es storage.E
|
|||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric6",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: []*Alert{},
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric7",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("templatedlabel", "{{ $externalURL }}"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: []*Alert{},
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
RecordingRule{
|
||||
Name: "recording-rule-1",
|
||||
Query: "vector(1)",
|
||||
|
@ -2158,6 +2207,13 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, es storage.E
|
|||
Health: "ok",
|
||||
Type: "recording",
|
||||
},
|
||||
RecordingRule{
|
||||
Name: "recording-rule-2",
|
||||
Query: "vector(1)",
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Health: "ok",
|
||||
Type: "recording",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2210,6 +2266,28 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, es storage.E
|
|||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric6",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: nil,
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric7",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("templatedlabel", "{{ $externalURL }}"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: nil,
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
RecordingRule{
|
||||
Name: "recording-rule-1",
|
||||
Query: "vector(1)",
|
||||
|
@ -2217,6 +2295,13 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, es storage.E
|
|||
Health: "ok",
|
||||
Type: "recording",
|
||||
},
|
||||
RecordingRule{
|
||||
Name: "recording-rule-2",
|
||||
Query: "vector(1)",
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Health: "ok",
|
||||
Type: "recording",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2276,6 +2361,28 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, es storage.E
|
|||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric6",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: []*Alert{},
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric7",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("templatedlabel", "{{ $externalURL }}"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: []*Alert{},
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2302,6 +2409,13 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, es storage.E
|
|||
Health: "ok",
|
||||
Type: "recording",
|
||||
},
|
||||
RecordingRule{
|
||||
Name: "recording-rule-2",
|
||||
Query: "vector(1)",
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Health: "ok",
|
||||
Type: "recording",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -2369,6 +2483,179 @@ func testEndpoints(t *testing.T, api *API, tr *testTargetRetriever, es storage.E
|
|||
},
|
||||
zeroFunc: rulesZeroFunc,
|
||||
},
|
||||
{
|
||||
endpoint: api.rules,
|
||||
query: url.Values{
|
||||
"match[]": []string{`{testlabel="rule"}`},
|
||||
},
|
||||
response: &RuleDiscovery{
|
||||
RuleGroups: []*RuleGroup{
|
||||
{
|
||||
Name: "grp",
|
||||
File: "/path/to/file",
|
||||
Interval: 1,
|
||||
Limit: 0,
|
||||
Rules: []Rule{
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric6",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: []*Alert{},
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
RecordingRule{
|
||||
Name: "recording-rule-2",
|
||||
Query: "vector(1)",
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Health: "ok",
|
||||
Type: "recording",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
zeroFunc: rulesZeroFunc,
|
||||
},
|
||||
{
|
||||
endpoint: api.rules,
|
||||
query: url.Values{
|
||||
"type": []string{"alert"},
|
||||
"match[]": []string{`{templatedlabel="{{ $externalURL }}"}`},
|
||||
},
|
||||
response: &RuleDiscovery{
|
||||
RuleGroups: []*RuleGroup{
|
||||
{
|
||||
Name: "grp",
|
||||
File: "/path/to/file",
|
||||
Interval: 1,
|
||||
Limit: 0,
|
||||
Rules: []Rule{
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric7",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("templatedlabel", "{{ $externalURL }}"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: []*Alert{},
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
zeroFunc: rulesZeroFunc,
|
||||
},
|
||||
{
|
||||
endpoint: api.rules,
|
||||
query: url.Values{
|
||||
"match[]": []string{`{testlabel="abc"}`},
|
||||
},
|
||||
response: &RuleDiscovery{
|
||||
RuleGroups: []*RuleGroup{},
|
||||
},
|
||||
},
|
||||
// This is testing OR condition, the api response should return rule if it matches one of the label selector
|
||||
{
|
||||
endpoint: api.rules,
|
||||
query: url.Values{
|
||||
"match[]": []string{`{testlabel="abc"}`, `{testlabel="rule"}`},
|
||||
},
|
||||
response: &RuleDiscovery{
|
||||
RuleGroups: []*RuleGroup{
|
||||
{
|
||||
Name: "grp",
|
||||
File: "/path/to/file",
|
||||
Interval: 1,
|
||||
Limit: 0,
|
||||
Rules: []Rule{
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric6",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: []*Alert{},
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
RecordingRule{
|
||||
Name: "recording-rule-2",
|
||||
Query: "vector(1)",
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Health: "ok",
|
||||
Type: "recording",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
zeroFunc: rulesZeroFunc,
|
||||
},
|
||||
{
|
||||
endpoint: api.rules,
|
||||
query: url.Values{
|
||||
"type": []string{"record"},
|
||||
"match[]": []string{`{testlabel="rule"}`},
|
||||
},
|
||||
response: &RuleDiscovery{
|
||||
RuleGroups: []*RuleGroup{
|
||||
{
|
||||
Name: "grp",
|
||||
File: "/path/to/file",
|
||||
Interval: 1,
|
||||
Limit: 0,
|
||||
Rules: []Rule{
|
||||
RecordingRule{
|
||||
Name: "recording-rule-2",
|
||||
Query: "vector(1)",
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Health: "ok",
|
||||
Type: "recording",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
zeroFunc: rulesZeroFunc,
|
||||
},
|
||||
{
|
||||
endpoint: api.rules,
|
||||
query: url.Values{
|
||||
"type": []string{"alert"},
|
||||
"match[]": []string{`{testlabel="rule"}`},
|
||||
},
|
||||
response: &RuleDiscovery{
|
||||
RuleGroups: []*RuleGroup{
|
||||
{
|
||||
Name: "grp",
|
||||
File: "/path/to/file",
|
||||
Interval: 1,
|
||||
Limit: 0,
|
||||
Rules: []Rule{
|
||||
AlertingRule{
|
||||
State: "inactive",
|
||||
Name: "test_metric6",
|
||||
Query: "up == 1",
|
||||
Duration: 1,
|
||||
Labels: labels.FromStrings("testlabel", "rule"),
|
||||
Annotations: labels.Labels{},
|
||||
Alerts: []*Alert{},
|
||||
Health: "ok",
|
||||
Type: "alerting",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
zeroFunc: rulesZeroFunc,
|
||||
},
|
||||
{
|
||||
endpoint: api.queryExemplars,
|
||||
query: url.Values{
|
||||
|
|
Loading…
Reference in a new issue