| 
									
										
										
										
											2016-08-13 06:11:52 +08:00
										 |  |  | // Copyright 2016 The Prometheus Authors
 | 
					
						
							|  |  |  | // Licensed under the Apache License, Version 2.0 (the "License");
 | 
					
						
							|  |  |  | // you may not use this file except in compliance with the License.
 | 
					
						
							|  |  |  | // You may obtain a copy of the License at
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Unless required by applicable law or agreed to in writing, software
 | 
					
						
							|  |  |  | // distributed under the License is distributed on an "AS IS" BASIS,
 | 
					
						
							|  |  |  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
					
						
							|  |  |  | // See the License for the specific language governing permissions and
 | 
					
						
							|  |  |  | // limitations under the License.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 08:52:59 +08:00
										 |  |  | package rules | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-12-18 20:29:35 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-10-27 18:06:53 +08:00
										 |  |  | 	"html/template" | 
					
						
							| 
									
										
										
										
											2016-08-12 08:52:59 +08:00
										 |  |  | 	"testing" | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-08-12 08:52:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-12 00:17:59 +08:00
										 |  |  | 	"github.com/go-kit/log" | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							| 
									
										
										
										
											2020-10-22 17:00:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-30 00:31:14 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/pkg/labels" | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/pkg/timestamp" | 
					
						
							| 
									
										
										
										
											2016-08-12 08:52:59 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/promql" | 
					
						
							| 
									
										
										
										
											2020-02-04 02:23:07 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/promql/parser" | 
					
						
							| 
									
										
										
										
											2019-12-18 20:29:35 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/util/teststorage" | 
					
						
							| 
									
										
										
										
											2016-08-12 08:52:59 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestAlertingRuleHTMLSnippet(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2020-02-04 02:23:07 +08:00
										 |  |  | 	expr, err := parser.ParseExpr(`foo{html="<b>BOLD<b>"}`) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 	rule := NewAlertingRule("testrule", expr, 0, labels.FromStrings("html", "<b>BOLD</b>"), labels.FromStrings("html", "<b>BOLD</b>"), nil, "", false, nil) | 
					
						
							| 
									
										
										
										
											2016-08-12 08:52:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-27 18:06:53 +08:00
										 |  |  | 	const want = template.HTML(`alert: <a href="/test/prefix/graph?g0.expr=ALERTS%7Balertname%3D%22testrule%22%7D&g0.tab=1">testrule</a> | 
					
						
							| 
									
										
										
										
											2017-10-05 18:16:15 +08:00
										 |  |  | expr: <a href="/test/prefix/graph?g0.expr=foo%7Bhtml%3D%22%3Cb%3EBOLD%3Cb%3E%22%7D&g0.tab=1">foo{html="<b>BOLD<b>"}</a> | 
					
						
							| 
									
										
										
										
											2017-07-08 17:38:02 +08:00
										 |  |  | labels: | 
					
						
							|  |  |  |   html: '<b>BOLD</b>' | 
					
						
							|  |  |  | annotations: | 
					
						
							|  |  |  |   html: '<b>BOLD</b>' | 
					
						
							| 
									
										
										
										
											2020-10-27 18:06:53 +08:00
										 |  |  | `) | 
					
						
							| 
									
										
										
										
											2016-09-04 03:08:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-12 08:52:59 +08:00
										 |  |  | 	got := rule.HTMLSnippet("/test/prefix") | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.Equal(t, want, got, "incorrect HTML snippet; want:\n\n|%v|\n\ngot:\n\n|%v|", want, got) | 
					
						
							| 
									
										
										
										
											2016-08-12 08:52:59 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 20:16:52 +08:00
										 |  |  | func TestAlertingRuleState(t *testing.T) { | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		name   string | 
					
						
							|  |  |  | 		active map[uint64]*Alert | 
					
						
							|  |  |  | 		want   AlertState | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "MaxStateFiring", | 
					
						
							|  |  |  | 			active: map[uint64]*Alert{ | 
					
						
							|  |  |  | 				0: {State: StatePending}, | 
					
						
							|  |  |  | 				1: {State: StateFiring}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			want: StateFiring, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "MaxStatePending", | 
					
						
							|  |  |  | 			active: map[uint64]*Alert{ | 
					
						
							|  |  |  | 				0: {State: StateInactive}, | 
					
						
							|  |  |  | 				1: {State: StatePending}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			want: StatePending, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			name: "MaxStateInactive", | 
					
						
							|  |  |  | 			active: map[uint64]*Alert{ | 
					
						
							|  |  |  | 				0: {State: StateInactive}, | 
					
						
							|  |  |  | 				1: {State: StateInactive}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			want: StateInactive, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, test := range tests { | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		rule := NewAlertingRule(test.name, nil, 0, nil, nil, nil, "", true, nil) | 
					
						
							| 
									
										
										
										
											2020-06-29 20:16:52 +08:00
										 |  |  | 		rule.active = test.active | 
					
						
							|  |  |  | 		got := rule.State() | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 		require.Equal(t, test.want, got, "test case %d unexpected AlertState, want:%d got:%d", i, test.want, got) | 
					
						
							| 
									
										
										
										
											2020-06-29 20:16:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | func TestAlertingRuleLabelsUpdate(t *testing.T) { | 
					
						
							|  |  |  | 	suite, err := promql.NewTest(t, ` | 
					
						
							|  |  |  | 		load 1m | 
					
						
							|  |  |  | 			http_requests{job="app-server", instance="0"}	75 85 70 70 | 
					
						
							|  |  |  | 	`) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 	defer suite.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, suite.Run()) | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-04 02:23:07 +08:00
										 |  |  | 	expr, err := parser.ParseExpr(`http_requests < 100`) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rule := NewAlertingRule( | 
					
						
							|  |  |  | 		"HTTPRequestRateLow", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							|  |  |  | 		// Basing alerting rule labels off of a value that can change is a very bad idea.
 | 
					
						
							|  |  |  | 		// If an alert is going back and forth between two label values it will never fire.
 | 
					
						
							|  |  |  | 		// Instead, you should write two alerts with constant labels.
 | 
					
						
							|  |  |  | 		labels.FromStrings("severity", "{{ if lt $value 80.0 }}critical{{ else }}warning{{ end }}"), | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		nil, nil, "", true, nil, | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	results := []promql.Vector{ | 
					
						
							| 
									
										
										
										
											2019-01-17 06:28:08 +08:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				Metric: labels.FromStrings( | 
					
						
							|  |  |  | 					"__name__", "ALERTS", | 
					
						
							|  |  |  | 					"alertname", "HTTPRequestRateLow", | 
					
						
							|  |  |  | 					"alertstate", "pending", | 
					
						
							|  |  |  | 					"instance", "0", | 
					
						
							|  |  |  | 					"job", "app-server", | 
					
						
							|  |  |  | 					"severity", "critical", | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-01-17 06:28:08 +08:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				Metric: labels.FromStrings( | 
					
						
							|  |  |  | 					"__name__", "ALERTS", | 
					
						
							|  |  |  | 					"alertname", "HTTPRequestRateLow", | 
					
						
							|  |  |  | 					"alertstate", "pending", | 
					
						
							|  |  |  | 					"instance", "0", | 
					
						
							|  |  |  | 					"job", "app-server", | 
					
						
							|  |  |  | 					"severity", "warning", | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-01-17 06:28:08 +08:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				Metric: labels.FromStrings( | 
					
						
							|  |  |  | 					"__name__", "ALERTS", | 
					
						
							|  |  |  | 					"alertname", "HTTPRequestRateLow", | 
					
						
							|  |  |  | 					"alertstate", "pending", | 
					
						
							|  |  |  | 					"instance", "0", | 
					
						
							|  |  |  | 					"job", "app-server", | 
					
						
							|  |  |  | 					"severity", "critical", | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-01-17 06:28:08 +08:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				Metric: labels.FromStrings( | 
					
						
							|  |  |  | 					"__name__", "ALERTS", | 
					
						
							|  |  |  | 					"alertname", "HTTPRequestRateLow", | 
					
						
							|  |  |  | 					"alertstate", "firing", | 
					
						
							|  |  |  | 					"instance", "0", | 
					
						
							|  |  |  | 					"job", "app-server", | 
					
						
							|  |  |  | 					"severity", "critical", | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	baseTime := time.Unix(0, 0) | 
					
						
							|  |  |  | 	for i, result := range results { | 
					
						
							|  |  |  | 		t.Logf("case %d", i) | 
					
						
							|  |  |  | 		evalTime := baseTime.Add(time.Duration(i) * time.Minute) | 
					
						
							|  |  |  | 		result[0].Point.T = timestamp.FromTime(evalTime) | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 		require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
 | 
					
						
							|  |  |  | 		for _, smpl := range res { | 
					
						
							|  |  |  | 			smplName := smpl.Metric.Get("__name__") | 
					
						
							|  |  |  | 			if smplName == "ALERTS" { | 
					
						
							|  |  |  | 				filteredRes = append(filteredRes, smpl) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// If not 'ALERTS', it has to be 'ALERTS_FOR_STATE'.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 				require.Equal(t, "ALERTS_FOR_STATE", smplName) | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 		require.Equal(t, result, filteredRes) | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestAlertingRuleExternalLabelsInTemplate(t *testing.T) { | 
					
						
							|  |  |  | 	suite, err := promql.NewTest(t, ` | 
					
						
							|  |  |  | 		load 1m | 
					
						
							|  |  |  | 			http_requests{job="app-server", instance="0"}	75 85 70 70 | 
					
						
							|  |  |  | 	`) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 	defer suite.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, suite.Run()) | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-04 02:23:07 +08:00
										 |  |  | 	expr, err := parser.ParseExpr(`http_requests < 100`) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ruleWithoutExternalLabels := NewAlertingRule( | 
					
						
							|  |  |  | 		"ExternalLabelDoesNotExist", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							|  |  |  | 		labels.FromStrings("templated_label", "There are {{ len $externalLabels }} external Labels, of which foo is {{ $externalLabels.foo }}."), | 
					
						
							|  |  |  | 		nil, | 
					
						
							|  |  |  | 		nil, | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		"", | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	ruleWithExternalLabels := NewAlertingRule( | 
					
						
							|  |  |  | 		"ExternalLabelExists", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							|  |  |  | 		labels.FromStrings("templated_label", "There are {{ len $externalLabels }} external Labels, of which foo is {{ $externalLabels.foo }}."), | 
					
						
							|  |  |  | 		nil, | 
					
						
							|  |  |  | 		labels.FromStrings("foo", "bar", "dings", "bums"), | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		"", | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	result := promql.Vector{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Metric: labels.FromStrings( | 
					
						
							|  |  |  | 				"__name__", "ALERTS", | 
					
						
							|  |  |  | 				"alertname", "ExternalLabelDoesNotExist", | 
					
						
							|  |  |  | 				"alertstate", "pending", | 
					
						
							|  |  |  | 				"instance", "0", | 
					
						
							|  |  |  | 				"job", "app-server", | 
					
						
							|  |  |  | 				"templated_label", "There are 0 external Labels, of which foo is .", | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Metric: labels.FromStrings( | 
					
						
							|  |  |  | 				"__name__", "ALERTS", | 
					
						
							|  |  |  | 				"alertname", "ExternalLabelExists", | 
					
						
							|  |  |  | 				"alertstate", "pending", | 
					
						
							|  |  |  | 				"instance", "0", | 
					
						
							|  |  |  | 				"job", "app-server", | 
					
						
							|  |  |  | 				"templated_label", "There are 2 external Labels, of which foo is bar.", | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	evalTime := time.Unix(0, 0) | 
					
						
							|  |  |  | 	result[0].Point.T = timestamp.FromTime(evalTime) | 
					
						
							|  |  |  | 	result[1].Point.T = timestamp.FromTime(evalTime) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
 | 
					
						
							|  |  |  | 	res, err := ruleWithoutExternalLabels.Eval( | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0, | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 	for _, smpl := range res { | 
					
						
							|  |  |  | 		smplName := smpl.Metric.Get("__name__") | 
					
						
							|  |  |  | 		if smplName == "ALERTS" { | 
					
						
							|  |  |  | 			filteredRes = append(filteredRes, smpl) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// If not 'ALERTS', it has to be 'ALERTS_FOR_STATE'.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 			require.Equal(t, "ALERTS_FOR_STATE", smplName) | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res, err = ruleWithExternalLabels.Eval( | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0, | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 	for _, smpl := range res { | 
					
						
							|  |  |  | 		smplName := smpl.Metric.Get("__name__") | 
					
						
							|  |  |  | 		if smplName == "ALERTS" { | 
					
						
							|  |  |  | 			filteredRes = append(filteredRes, smpl) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// If not 'ALERTS', it has to be 'ALERTS_FOR_STATE'.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 			require.Equal(t, "ALERTS_FOR_STATE", smplName) | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.Equal(t, result, filteredRes) | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | func TestAlertingRuleExternalURLInTemplate(t *testing.T) { | 
					
						
							|  |  |  | 	suite, err := promql.NewTest(t, ` | 
					
						
							|  |  |  | 		load 1m | 
					
						
							|  |  |  | 			http_requests{job="app-server", instance="0"}	75 85 70 70 | 
					
						
							|  |  |  | 	`) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	defer suite.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	require.NoError(t, suite.Run()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	expr, err := parser.ParseExpr(`http_requests < 100`) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ruleWithoutExternalURL := NewAlertingRule( | 
					
						
							|  |  |  | 		"ExternalURLDoesNotExist", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							|  |  |  | 		labels.FromStrings("templated_label", "The external URL is {{ $externalURL }}."), | 
					
						
							|  |  |  | 		nil, | 
					
						
							|  |  |  | 		nil, | 
					
						
							|  |  |  | 		"", | 
					
						
							|  |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	ruleWithExternalURL := NewAlertingRule( | 
					
						
							|  |  |  | 		"ExternalURLExists", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							|  |  |  | 		labels.FromStrings("templated_label", "The external URL is {{ $externalURL }}."), | 
					
						
							|  |  |  | 		nil, | 
					
						
							|  |  |  | 		nil, | 
					
						
							|  |  |  | 		"http://localhost:1234", | 
					
						
							|  |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	result := promql.Vector{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Metric: labels.FromStrings( | 
					
						
							|  |  |  | 				"__name__", "ALERTS", | 
					
						
							|  |  |  | 				"alertname", "ExternalURLDoesNotExist", | 
					
						
							|  |  |  | 				"alertstate", "pending", | 
					
						
							|  |  |  | 				"instance", "0", | 
					
						
							|  |  |  | 				"job", "app-server", | 
					
						
							|  |  |  | 				"templated_label", "The external URL is .", | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Metric: labels.FromStrings( | 
					
						
							|  |  |  | 				"__name__", "ALERTS", | 
					
						
							|  |  |  | 				"alertname", "ExternalURLExists", | 
					
						
							|  |  |  | 				"alertstate", "pending", | 
					
						
							|  |  |  | 				"instance", "0", | 
					
						
							|  |  |  | 				"job", "app-server", | 
					
						
							|  |  |  | 				"templated_label", "The external URL is http://localhost:1234.", | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	evalTime := time.Unix(0, 0) | 
					
						
							|  |  |  | 	result[0].Point.T = timestamp.FromTime(evalTime) | 
					
						
							|  |  |  | 	result[1].Point.T = timestamp.FromTime(evalTime) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
 | 
					
						
							|  |  |  | 	res, err := ruleWithoutExternalURL.Eval( | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0, | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	for _, smpl := range res { | 
					
						
							|  |  |  | 		smplName := smpl.Metric.Get("__name__") | 
					
						
							|  |  |  | 		if smplName == "ALERTS" { | 
					
						
							|  |  |  | 			filteredRes = append(filteredRes, smpl) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// If not 'ALERTS', it has to be 'ALERTS_FOR_STATE'.
 | 
					
						
							|  |  |  | 			require.Equal(t, "ALERTS_FOR_STATE", smplName) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res, err = ruleWithExternalURL.Eval( | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0, | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	for _, smpl := range res { | 
					
						
							|  |  |  | 		smplName := smpl.Metric.Get("__name__") | 
					
						
							|  |  |  | 		if smplName == "ALERTS" { | 
					
						
							|  |  |  | 			filteredRes = append(filteredRes, smpl) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// If not 'ALERTS', it has to be 'ALERTS_FOR_STATE'.
 | 
					
						
							|  |  |  | 			require.Equal(t, "ALERTS_FOR_STATE", smplName) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	require.Equal(t, result, filteredRes) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | func TestAlertingRuleEmptyLabelFromTemplate(t *testing.T) { | 
					
						
							|  |  |  | 	suite, err := promql.NewTest(t, ` | 
					
						
							|  |  |  | 		load 1m | 
					
						
							|  |  |  | 			http_requests{job="app-server", instance="0"}	75 85 70 70 | 
					
						
							|  |  |  | 	`) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 	defer suite.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, suite.Run()) | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-04 02:23:07 +08:00
										 |  |  | 	expr, err := parser.ParseExpr(`http_requests < 100`) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rule := NewAlertingRule( | 
					
						
							|  |  |  | 		"EmptyLabel", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							|  |  |  | 		labels.FromStrings("empty_label", ""), | 
					
						
							|  |  |  | 		nil, | 
					
						
							|  |  |  | 		nil, | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		"", | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	result := promql.Vector{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			Metric: labels.FromStrings( | 
					
						
							|  |  |  | 				"__name__", "ALERTS", | 
					
						
							|  |  |  | 				"alertname", "EmptyLabel", | 
					
						
							|  |  |  | 				"alertstate", "pending", | 
					
						
							|  |  |  | 				"instance", "0", | 
					
						
							|  |  |  | 				"job", "app-server", | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	evalTime := time.Unix(0, 0) | 
					
						
							|  |  |  | 	result[0].Point.T = timestamp.FromTime(evalTime) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var filteredRes promql.Vector // After removing 'ALERTS_FOR_STATE' samples.
 | 
					
						
							|  |  |  | 	res, err := rule.Eval( | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0, | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 	) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 	for _, smpl := range res { | 
					
						
							|  |  |  | 		smplName := smpl.Metric.Get("__name__") | 
					
						
							|  |  |  | 		if smplName == "ALERTS" { | 
					
						
							|  |  |  | 			filteredRes = append(filteredRes, smpl) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// If not 'ALERTS', it has to be 'ALERTS_FOR_STATE'.
 | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 			require.Equal(t, "ALERTS_FOR_STATE", smplName) | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.Equal(t, result, filteredRes) | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-12-18 20:29:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestAlertingRuleDuplicate(t *testing.T) { | 
					
						
							|  |  |  | 	storage := teststorage.New(t) | 
					
						
							|  |  |  | 	defer storage.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts := promql.EngineOpts{ | 
					
						
							| 
									
										
										
										
											2020-01-29 04:38:49 +08:00
										 |  |  | 		Logger:     nil, | 
					
						
							|  |  |  | 		Reg:        nil, | 
					
						
							|  |  |  | 		MaxSamples: 10, | 
					
						
							|  |  |  | 		Timeout:    10 * time.Second, | 
					
						
							| 
									
										
										
										
											2019-12-18 20:29:35 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	engine := promql.NewEngine(opts) | 
					
						
							|  |  |  | 	ctx, cancelCtx := context.WithCancel(context.Background()) | 
					
						
							|  |  |  | 	defer cancelCtx() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	now := time.Now() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-04 02:23:07 +08:00
										 |  |  | 	expr, _ := parser.ParseExpr(`vector(0) or label_replace(vector(0),"test","x","","")`) | 
					
						
							| 
									
										
										
										
											2019-12-18 20:29:35 +08:00
										 |  |  | 	rule := NewAlertingRule( | 
					
						
							|  |  |  | 		"foo", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							|  |  |  | 		labels.FromStrings("test", "test"), | 
					
						
							|  |  |  | 		nil, | 
					
						
							|  |  |  | 		nil, | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		"", | 
					
						
							| 
									
										
										
										
											2019-12-18 20:29:35 +08:00
										 |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 	_, err := rule.Eval(ctx, now, EngineQueryFunc(engine, storage), nil, 0) | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	require.Error(t, err) | 
					
						
							|  |  |  | 	require.EqualError(t, err, "vector contains metrics with the same labelset after applying alert labels") | 
					
						
							| 
									
										
										
										
											2019-12-18 20:29:35 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestAlertingRuleLimit(t *testing.T) { | 
					
						
							|  |  |  | 	storage := teststorage.New(t) | 
					
						
							|  |  |  | 	defer storage.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts := promql.EngineOpts{ | 
					
						
							|  |  |  | 		Logger:     nil, | 
					
						
							|  |  |  | 		Reg:        nil, | 
					
						
							|  |  |  | 		MaxSamples: 10, | 
					
						
							|  |  |  | 		Timeout:    10 * time.Second, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	engine := promql.NewEngine(opts) | 
					
						
							|  |  |  | 	ctx, cancelCtx := context.WithCancel(context.Background()) | 
					
						
							|  |  |  | 	defer cancelCtx() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	now := time.Now() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	suite := []struct { | 
					
						
							|  |  |  | 		limit int | 
					
						
							|  |  |  | 		err   string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			limit: 0, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			limit: 1, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			limit: -1, | 
					
						
							|  |  |  | 			err:   "exceeded limit of -1 with 1 alerts", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range suite { | 
					
						
							|  |  |  | 		expr, _ := parser.ParseExpr(`1`) | 
					
						
							|  |  |  | 		rule := NewAlertingRule( | 
					
						
							|  |  |  | 			"foo", | 
					
						
							|  |  |  | 			expr, | 
					
						
							|  |  |  | 			time.Minute, | 
					
						
							|  |  |  | 			labels.FromStrings("test", "test"), | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			"", | 
					
						
							|  |  |  | 			true, log.NewNopLogger(), | 
					
						
							|  |  |  | 		) | 
					
						
							|  |  |  | 		_, err := rule.Eval(ctx, now, EngineQueryFunc(engine, storage), nil, test.limit) | 
					
						
							|  |  |  | 		if test.err == "" { | 
					
						
							|  |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			require.Equal(t, test.err, err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |