| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2022-06-17 15:54:25 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2022-10-07 22:58:17 +08:00
										 |  |  | 	"github.com/prometheus/common/model" | 
					
						
							| 
									
										
										
										
											2020-10-29 17:43:23 +08:00
										 |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							| 
									
										
										
										
											2020-10-22 17:00:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-08 22:23:17 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/model/labels" | 
					
						
							| 
									
										
										
										
											2022-10-07 22:58:17 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/model/relabel" | 
					
						
							| 
									
										
										
										
											2021-11-08 22:23:17 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/model/timestamp" | 
					
						
							| 
									
										
										
										
											2022-10-07 22:58:17 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/notifier" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2022-03-29 08:16:46 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/storage" | 
					
						
							| 
									
										
										
										
											2019-12-18 20:29:35 +08:00
										 |  |  | 	"github.com/prometheus/prometheus/util/teststorage" | 
					
						
							| 
									
										
										
										
											2016-08-12 08:52:59 +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 { | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		rule := NewAlertingRule(test.name, nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), labels.EmptyLabels(), "", 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 | 
					
						
							| 
									
										
										
										
											2023-01-19 17:36:01 +08:00
										 |  |  | 			http_requests{job="app-server", instance="0"}	75 85 70 70 stale | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 	`) | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 		// 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 }}"), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 		labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil, | 
					
						
							| 
									
										
										
										
											2018-08-15 15:52:08 +08:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	results := []promql.Vector{ | 
					
						
							| 
									
										
										
										
											2019-01-17 06:28:08 +08:00
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2021-11-18 02:57:31 +08:00
										 |  |  | 			promql.Sample{ | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2021-11-18 02:57:31 +08:00
										 |  |  | 			promql.Sample{ | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2021-11-18 02:57:31 +08:00
										 |  |  | 			promql.Sample{ | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2021-11-18 02:57:31 +08:00
										 |  |  | 			promql.Sample{ | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-01-19 17:36:01 +08:00
										 |  |  | 	evalTime := baseTime.Add(time.Duration(len(results)) * time.Minute) | 
					
						
							|  |  |  | 	res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	require.Equal(t, 0, len(res)) | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 		labels.FromStrings("templated_label", "There are {{ len $externalLabels }} external Labels, of which foo is {{ $externalLabels.foo }}."), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 		labels.EmptyLabels(), | 
					
						
							|  |  |  | 		labels.EmptyLabels(), | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 		labels.FromStrings("templated_label", "There are {{ len $externalLabels }} external Labels, of which foo is {{ $externalLabels.foo }}."), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 		labels.EmptyLabels(), | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 		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{ | 
					
						
							| 
									
										
										
										
											2021-11-18 02:57:31 +08:00
										 |  |  | 		promql.Sample{ | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 			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}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2021-11-18 02:57:31 +08:00
										 |  |  | 		promql.Sample{ | 
					
						
							| 
									
										
										
										
											2019-04-20 08:19:06 +08:00
										 |  |  | 			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, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		labels.FromStrings("templated_label", "The external URL is {{ $externalURL }}."), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 		labels.EmptyLabels(), | 
					
						
							|  |  |  | 		labels.EmptyLabels(), | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		"", | 
					
						
							|  |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	ruleWithExternalURL := NewAlertingRule( | 
					
						
							|  |  |  | 		"ExternalURLExists", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		labels.FromStrings("templated_label", "The external URL is {{ $externalURL }}."), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 		labels.EmptyLabels(), | 
					
						
							|  |  |  | 		labels.EmptyLabels(), | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		"http://localhost:1234", | 
					
						
							|  |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	result := promql.Vector{ | 
					
						
							| 
									
										
										
										
											2021-11-18 02:57:31 +08:00
										 |  |  | 		promql.Sample{ | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 			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}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2021-11-18 02:57:31 +08:00
										 |  |  | 		promql.Sample{ | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 			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, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 		labels.FromStrings("empty_label", ""), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 		labels.EmptyLabels(), | 
					
						
							|  |  |  | 		labels.EmptyLabels(), | 
					
						
							| 
									
										
										
										
											2021-05-31 11:35:41 +08:00
										 |  |  | 		"", | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	result := promql.Vector{ | 
					
						
							| 
									
										
										
										
											2021-11-18 02:57:31 +08:00
										 |  |  | 		promql.Sample{ | 
					
						
							| 
									
										
										
										
											2019-08-13 18:19:17 +08:00
										 |  |  | 			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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 18:58:37 +08:00
										 |  |  | func TestAlertingRuleQueryInTemplate(t *testing.T) { | 
					
						
							|  |  |  | 	suite, err := promql.NewTest(t, ` | 
					
						
							|  |  |  | 		load 1m | 
					
						
							|  |  |  | 			http_requests{job="app-server", instance="0"}	70 85 70 70 | 
					
						
							|  |  |  | 	`) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	defer suite.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	require.NoError(t, suite.Run()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	expr, err := parser.ParseExpr(`sum(http_requests) < 100`) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ruleWithQueryInTemplate := NewAlertingRule( | 
					
						
							|  |  |  | 		"ruleWithQueryInTemplate", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2022-07-19 18:58:37 +08:00
										 |  |  | 		labels.FromStrings("label", "value"), | 
					
						
							|  |  |  | 		labels.FromStrings("templated_label", `{{- with "sort(sum(http_requests) by (instance))" | query -}} | 
					
						
							|  |  |  | {{- range $i,$v := . -}} | 
					
						
							|  |  |  | instance: {{ $v.Labels.instance }}, value: {{ printf "%.0f" $v.Value }}; | 
					
						
							|  |  |  | {{- end -}} | 
					
						
							|  |  |  | {{- end -}} | 
					
						
							|  |  |  | `), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 		labels.EmptyLabels(), | 
					
						
							| 
									
										
										
										
											2022-07-19 18:58:37 +08:00
										 |  |  | 		"", | 
					
						
							|  |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	evalTime := time.Unix(0, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	startQueryCh := make(chan struct{}) | 
					
						
							|  |  |  | 	getDoneCh := make(chan struct{}) | 
					
						
							|  |  |  | 	slowQueryFunc := func(ctx context.Context, q string, ts time.Time) (promql.Vector, error) { | 
					
						
							|  |  |  | 		if q == "sort(sum(http_requests) by (instance))" { | 
					
						
							|  |  |  | 			// This is a minimum reproduction of issue 10703, expand template with query.
 | 
					
						
							|  |  |  | 			close(startQueryCh) | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-getDoneCh: | 
					
						
							|  |  |  | 			case <-time.After(time.Millisecond * 10): | 
					
						
							|  |  |  | 				// Assert no blocking when template expanding.
 | 
					
						
							|  |  |  | 				require.Fail(t, "unexpected blocking when template expanding.") | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return EngineQueryFunc(suite.QueryEngine(), suite.Storage())(ctx, q, ts) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		<-startQueryCh | 
					
						
							|  |  |  | 		_ = ruleWithQueryInTemplate.Health() | 
					
						
							|  |  |  | 		_ = ruleWithQueryInTemplate.LastError() | 
					
						
							|  |  |  | 		_ = ruleWithQueryInTemplate.GetEvaluationDuration() | 
					
						
							|  |  |  | 		_ = ruleWithQueryInTemplate.GetEvaluationTimestamp() | 
					
						
							|  |  |  | 		close(getDoneCh) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	_, err = ruleWithQueryInTemplate.Eval( | 
					
						
							|  |  |  | 		suite.Context(), evalTime, slowQueryFunc, nil, 0, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkAlertingRuleAtomicField(b *testing.B) { | 
					
						
							|  |  |  | 	b.ReportAllocs() | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 	rule := NewAlertingRule("bench", nil, 0, 0, labels.EmptyLabels(), labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil) | 
					
						
							| 
									
										
										
										
											2022-07-19 18:58:37 +08:00
										 |  |  | 	done := make(chan struct{}) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 			rule.GetEvaluationTimestamp() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		close(done) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	b.RunParallel(func(pb *testing.PB) { | 
					
						
							|  |  |  | 		for pb.Next() { | 
					
						
							|  |  |  | 			rule.SetEvaluationTimestamp(time.Now()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	<-done | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2019-12-18 20:29:35 +08:00
										 |  |  | 		labels.FromStrings("test", "test"), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 		labels.EmptyLabels(), | 
					
						
							|  |  |  | 		labels.EmptyLabels(), | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2021-10-22 05:14:17 +08:00
										 |  |  | 	suite, err := promql.NewTest(t, ` | 
					
						
							|  |  |  | 		load 1m | 
					
						
							|  |  |  | 			metric{label="1"} 1 | 
					
						
							|  |  |  | 			metric{label="2"} 1 | 
					
						
							|  |  |  | 	`) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	defer suite.Close() | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-22 05:14:17 +08:00
										 |  |  | 	require.NoError(t, suite.Run()) | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-22 05:14:17 +08:00
										 |  |  | 	tests := []struct { | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		limit int | 
					
						
							|  |  |  | 		err   string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			limit: 0, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2021-10-22 05:14:17 +08:00
										 |  |  | 			limit: -1, | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2021-10-22 05:14:17 +08:00
										 |  |  | 			limit: 2, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			limit: 1, | 
					
						
							|  |  |  | 			err:   "exceeded limit of 1 with 2 alerts", | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-22 05:14:17 +08:00
										 |  |  | 	expr, _ := parser.ParseExpr(`metric > 0`) | 
					
						
							|  |  |  | 	rule := NewAlertingRule( | 
					
						
							|  |  |  | 		"foo", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2021-10-22 05:14:17 +08:00
										 |  |  | 		labels.FromStrings("test", "test"), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 		labels.EmptyLabels(), | 
					
						
							|  |  |  | 		labels.EmptyLabels(), | 
					
						
							| 
									
										
										
										
											2021-10-22 05:14:17 +08:00
										 |  |  | 		"", | 
					
						
							|  |  |  | 		true, log.NewNopLogger(), | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	evalTime := time.Unix(0, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range tests { | 
					
						
							|  |  |  | 		_, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, test.limit) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			require.EqualError(t, err, test.err) | 
					
						
							|  |  |  | 		} else if test.err != "" { | 
					
						
							|  |  |  | 			t.Errorf("Expected errror %s, got none", test.err) | 
					
						
							| 
									
										
										
										
											2021-09-15 15:48:26 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-03-29 08:16:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestQueryForStateSeries(t *testing.T) { | 
					
						
							|  |  |  | 	testError := errors.New("test error") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	type testInput struct { | 
					
						
							|  |  |  | 		selectMockFunction func(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet | 
					
						
							|  |  |  | 		expectedSeries     storage.Series | 
					
						
							|  |  |  | 		expectedError      error | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tests := []testInput{ | 
					
						
							|  |  |  | 		// Test for empty series.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			selectMockFunction: func(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet { | 
					
						
							|  |  |  | 				return storage.EmptySeriesSet() | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expectedSeries: nil, | 
					
						
							|  |  |  | 			expectedError:  nil, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test for error series.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			selectMockFunction: func(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet { | 
					
						
							|  |  |  | 				return storage.ErrSeriesSet(testError) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expectedSeries: nil, | 
					
						
							|  |  |  | 			expectedError:  testError, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// Test for mock series.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			selectMockFunction: func(sortSeries bool, hints *storage.SelectHints, matchers ...*labels.Matcher) storage.SeriesSet { | 
					
						
							|  |  |  | 				return storage.TestSeriesSet(storage.MockSeries( | 
					
						
							|  |  |  | 					[]int64{1, 2, 3}, | 
					
						
							|  |  |  | 					[]float64{1, 2, 3}, | 
					
						
							|  |  |  | 					[]string{"__name__", "ALERTS_FOR_STATE", "alertname", "TestRule", "severity", "critical"}, | 
					
						
							|  |  |  | 				)) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			expectedSeries: storage.MockSeries( | 
					
						
							|  |  |  | 				[]int64{1, 2, 3}, | 
					
						
							|  |  |  | 				[]float64{1, 2, 3}, | 
					
						
							|  |  |  | 				[]string{"__name__", "ALERTS_FOR_STATE", "alertname", "TestRule", "severity", "critical"}, | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			expectedError: nil, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	testFunc := func(tst testInput) { | 
					
						
							|  |  |  | 		querier := &storage.MockQuerier{ | 
					
						
							|  |  |  | 			SelectMockFunction: tst.selectMockFunction, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rule := NewAlertingRule( | 
					
						
							|  |  |  | 			"TestRule", | 
					
						
							|  |  |  | 			nil, | 
					
						
							|  |  |  | 			time.Minute, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 			0, | 
					
						
							| 
									
										
										
										
											2022-03-29 08:16:46 +08:00
										 |  |  | 			labels.FromStrings("severity", "critical"), | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 			labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil, | 
					
						
							| 
									
										
										
										
											2022-03-29 08:16:46 +08:00
										 |  |  | 		) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		alert := &Alert{ | 
					
						
							|  |  |  | 			State:       0, | 
					
						
							| 
									
										
										
										
											2022-07-22 00:44:35 +08:00
										 |  |  | 			Labels:      labels.EmptyLabels(), | 
					
						
							|  |  |  | 			Annotations: labels.EmptyLabels(), | 
					
						
							| 
									
										
										
										
											2022-03-29 08:16:46 +08:00
										 |  |  | 			Value:       0, | 
					
						
							|  |  |  | 			ActiveAt:    time.Time{}, | 
					
						
							|  |  |  | 			FiredAt:     time.Time{}, | 
					
						
							|  |  |  | 			ResolvedAt:  time.Time{}, | 
					
						
							|  |  |  | 			LastSentAt:  time.Time{}, | 
					
						
							|  |  |  | 			ValidUntil:  time.Time{}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		series, err := rule.QueryforStateSeries(alert, querier) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		require.Equal(t, tst.expectedSeries, series) | 
					
						
							|  |  |  | 		require.Equal(t, tst.expectedError, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tst := range tests { | 
					
						
							|  |  |  | 		testFunc(tst) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-10-07 22:58:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TestSendAlertsDontAffectActiveAlerts tests a fix for https://github.com/prometheus/prometheus/issues/11424.
 | 
					
						
							|  |  |  | func TestSendAlertsDontAffectActiveAlerts(t *testing.T) { | 
					
						
							|  |  |  | 	rule := NewAlertingRule( | 
					
						
							|  |  |  | 		"TestRule", | 
					
						
							|  |  |  | 		nil, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							| 
									
										
										
										
											2023-01-09 19:21:38 +08:00
										 |  |  | 		0, | 
					
						
							| 
									
										
										
										
											2022-10-07 22:58:17 +08:00
										 |  |  | 		labels.FromStrings("severity", "critical"), | 
					
						
							|  |  |  | 		labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set an active alert.
 | 
					
						
							|  |  |  | 	lbls := labels.FromStrings("a1", "1") | 
					
						
							|  |  |  | 	h := lbls.Hash() | 
					
						
							|  |  |  | 	al := &Alert{State: StateFiring, Labels: lbls, ActiveAt: time.Now()} | 
					
						
							|  |  |  | 	rule.active[h] = al | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	expr, err := parser.ParseExpr("foo") | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	rule.vector = expr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The relabel rule reproduced the bug here.
 | 
					
						
							|  |  |  | 	opts := notifier.Options{ | 
					
						
							|  |  |  | 		QueueCapacity: 1, | 
					
						
							|  |  |  | 		RelabelConfigs: []*relabel.Config{ | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				SourceLabels: model.LabelNames{"a1"}, | 
					
						
							|  |  |  | 				Regex:        relabel.MustNewRegexp("(.+)"), | 
					
						
							|  |  |  | 				TargetLabel:  "a1", | 
					
						
							|  |  |  | 				Replacement:  "bug", | 
					
						
							|  |  |  | 				Action:       "replace", | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	nm := notifier.NewManager(&opts, log.NewNopLogger()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	f := SendAlerts(nm, "") | 
					
						
							|  |  |  | 	notifyFunc := func(ctx context.Context, expr string, alerts ...*Alert) { | 
					
						
							|  |  |  | 		require.Len(t, alerts, 1) | 
					
						
							|  |  |  | 		require.Equal(t, al, alerts[0]) | 
					
						
							|  |  |  | 		f(ctx, expr, alerts...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rule.sendAlerts(context.Background(), time.Now(), 0, 0, notifyFunc) | 
					
						
							|  |  |  | 	nm.Stop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The relabel rule changes a1=1 to a1=bug.
 | 
					
						
							|  |  |  | 	// But the labels with the AlertingRule should not be changed.
 | 
					
						
							|  |  |  | 	require.Equal(t, labels.FromStrings("a1", "1"), rule.active[h].Labels) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-01-19 17:36:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestKeepFiringFor(t *testing.T) { | 
					
						
							|  |  |  | 	suite, err := promql.NewTest(t, ` | 
					
						
							|  |  |  | 		load 1m | 
					
						
							|  |  |  | 			http_requests{job="app-server", instance="0"}	75 85 70 70 10x5 | 
					
						
							|  |  |  | 	`) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	defer suite.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	require.NoError(t, suite.Run()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	expr, err := parser.ParseExpr(`http_requests > 50`) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rule := NewAlertingRule( | 
					
						
							|  |  |  | 		"HTTPRequestRateHigh", | 
					
						
							|  |  |  | 		expr, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							|  |  |  | 		time.Minute, | 
					
						
							|  |  |  | 		labels.EmptyLabels(), | 
					
						
							|  |  |  | 		labels.EmptyLabels(), labels.EmptyLabels(), "", true, nil, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	results := []promql.Vector{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			promql.Sample{ | 
					
						
							|  |  |  | 				Metric: labels.FromStrings( | 
					
						
							|  |  |  | 					"__name__", "ALERTS", | 
					
						
							|  |  |  | 					"alertname", "HTTPRequestRateHigh", | 
					
						
							|  |  |  | 					"alertstate", "pending", | 
					
						
							|  |  |  | 					"instance", "0", | 
					
						
							|  |  |  | 					"job", "app-server", | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			promql.Sample{ | 
					
						
							|  |  |  | 				Metric: labels.FromStrings( | 
					
						
							|  |  |  | 					"__name__", "ALERTS", | 
					
						
							|  |  |  | 					"alertname", "HTTPRequestRateHigh", | 
					
						
							|  |  |  | 					"alertstate", "firing", | 
					
						
							|  |  |  | 					"instance", "0", | 
					
						
							|  |  |  | 					"job", "app-server", | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			promql.Sample{ | 
					
						
							|  |  |  | 				Metric: labels.FromStrings( | 
					
						
							|  |  |  | 					"__name__", "ALERTS", | 
					
						
							|  |  |  | 					"alertname", "HTTPRequestRateHigh", | 
					
						
							|  |  |  | 					"alertstate", "firing", | 
					
						
							|  |  |  | 					"instance", "0", | 
					
						
							|  |  |  | 					"job", "app-server", | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			promql.Sample{ | 
					
						
							|  |  |  | 				Metric: labels.FromStrings( | 
					
						
							|  |  |  | 					"__name__", "ALERTS", | 
					
						
							|  |  |  | 					"alertname", "HTTPRequestRateHigh", | 
					
						
							|  |  |  | 					"alertstate", "firing", | 
					
						
							|  |  |  | 					"instance", "0", | 
					
						
							|  |  |  | 					"job", "app-server", | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				Point: promql.Point{V: 1}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// From now on the alert should keep firing.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			promql.Sample{ | 
					
						
							|  |  |  | 				Metric: labels.FromStrings( | 
					
						
							|  |  |  | 					"__name__", "ALERTS", | 
					
						
							|  |  |  | 					"alertname", "HTTPRequestRateHigh", | 
					
						
							|  |  |  | 					"alertstate", "firing", | 
					
						
							|  |  |  | 					"instance", "0", | 
					
						
							|  |  |  | 					"job", "app-server", | 
					
						
							|  |  |  | 				), | 
					
						
							|  |  |  | 				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) | 
					
						
							|  |  |  | 		res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		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'.
 | 
					
						
							|  |  |  | 				require.Equal(t, "ALERTS_FOR_STATE", smplName) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		require.Equal(t, result, filteredRes) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	evalTime := baseTime.Add(time.Duration(len(results)) * time.Minute) | 
					
						
							|  |  |  | 	res, err := rule.Eval(suite.Context(), evalTime, EngineQueryFunc(suite.QueryEngine(), suite.Storage()), nil, 0) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	require.Equal(t, 0, len(res)) | 
					
						
							|  |  |  | } |