diff --git a/frontend/public/locales/en-GB/alerts.json b/frontend/public/locales/en-GB/alerts.json
index a43d04ab59..86f21c8c78 100644
--- a/frontend/public/locales/en-GB/alerts.json
+++ b/frontend/public/locales/en-GB/alerts.json
@@ -53,6 +53,7 @@
"option_atleastonce": "at least once",
"option_onaverage": "on average",
"option_intotal": "in total",
+ "option_last": "last",
"option_above": "above",
"option_below": "below",
"option_equal": "is equal to",
diff --git a/frontend/public/locales/en-GB/rules.json b/frontend/public/locales/en-GB/rules.json
index 9d55a0ba0f..9ac3641c7a 100644
--- a/frontend/public/locales/en-GB/rules.json
+++ b/frontend/public/locales/en-GB/rules.json
@@ -40,6 +40,7 @@
"option_atleastonce": "at least once",
"option_onaverage": "on average",
"option_intotal": "in total",
+ "option_last": "last",
"option_above": "above",
"option_below": "below",
"option_equal": "is equal to",
diff --git a/frontend/public/locales/en/alerts.json b/frontend/public/locales/en/alerts.json
index e7ed6232ad..02d20a2977 100644
--- a/frontend/public/locales/en/alerts.json
+++ b/frontend/public/locales/en/alerts.json
@@ -53,6 +53,7 @@
"option_atleastonce": "at least once",
"option_onaverage": "on average",
"option_intotal": "in total",
+ "option_last": "last",
"option_above": "above",
"option_below": "below",
"option_equal": "is equal to",
diff --git a/frontend/public/locales/en/rules.json b/frontend/public/locales/en/rules.json
index 9d55a0ba0f..9ac3641c7a 100644
--- a/frontend/public/locales/en/rules.json
+++ b/frontend/public/locales/en/rules.json
@@ -40,6 +40,7 @@
"option_atleastonce": "at least once",
"option_onaverage": "on average",
"option_intotal": "in total",
+ "option_last": "last",
"option_above": "above",
"option_below": "below",
"option_equal": "is equal to",
diff --git a/frontend/src/container/FormAlertRules/RuleOptions.tsx b/frontend/src/container/FormAlertRules/RuleOptions.tsx
index 2ef6bba4c0..da265f34cc 100644
--- a/frontend/src/container/FormAlertRules/RuleOptions.tsx
+++ b/frontend/src/container/FormAlertRules/RuleOptions.tsx
@@ -103,6 +103,7 @@ function RuleOptions({
{t('option_allthetimes')}
{t('option_onaverage')}
{t('option_intotal')}
+ {t('option_last')}
);
diff --git a/pkg/query-service/rules/alerting.go b/pkg/query-service/rules/alerting.go
index cb5205f99e..77c9fbe219 100644
--- a/pkg/query-service/rules/alerting.go
+++ b/pkg/query-service/rules/alerting.go
@@ -112,6 +112,7 @@ const (
AllTheTimes MatchType = "2"
OnAverage MatchType = "3"
InTotal MatchType = "4"
+ Last MatchType = "5"
)
type RuleCondition struct {
diff --git a/pkg/query-service/rules/base_rule.go b/pkg/query-service/rules/base_rule.go
index a108938b1d..b82aab91b5 100644
--- a/pkg/query-service/rules/base_rule.go
+++ b/pkg/query-service/rules/base_rule.go
@@ -483,6 +483,27 @@ func (r *BaseRule) shouldAlert(series v3.Series) (Sample, bool) {
shouldAlert = true
}
}
+ case Last:
+ // If the last sample matches the condition, the rule is firing.
+ shouldAlert = false
+ alertSmpl = Sample{Point: Point{V: series.Points[len(series.Points)-1].Value}, Metric: lblsNormalized, MetricOrig: lbls}
+ if r.compareOp() == ValueIsAbove {
+ if series.Points[len(series.Points)-1].Value > r.targetVal() {
+ shouldAlert = true
+ }
+ } else if r.compareOp() == ValueIsBelow {
+ if series.Points[len(series.Points)-1].Value < r.targetVal() {
+ shouldAlert = true
+ }
+ } else if r.compareOp() == ValueIsEq {
+ if series.Points[len(series.Points)-1].Value == r.targetVal() {
+ shouldAlert = true
+ }
+ } else if r.compareOp() == ValueIsNotEq {
+ if series.Points[len(series.Points)-1].Value != r.targetVal() {
+ shouldAlert = true
+ }
+ }
}
return alertSmpl, shouldAlert
}
diff --git a/pkg/query-service/rules/threshold_rule_test.go b/pkg/query-service/rules/threshold_rule_test.go
index 65d020d25f..8f9554db52 100644
--- a/pkg/query-service/rules/threshold_rule_test.go
+++ b/pkg/query-service/rules/threshold_rule_test.go
@@ -677,6 +677,111 @@ func TestThresholdRuleShouldAlert(t *testing.T) {
matchType: "4", // InTotal
target: 20.0,
},
+ // Test cases for Last
+ // greater than last
+ {
+ values: v3.Series{
+ Points: []v3.Point{
+ {Value: 10.0},
+ {Value: 10.0},
+ },
+ },
+ expectAlert: true,
+ compareOp: "1", // Greater Than
+ matchType: "5", // Last
+ target: 5.0,
+ expectedAlertSample: v3.Point{Value: 10.0},
+ },
+ {
+ values: v3.Series{
+ Points: []v3.Point{
+ {Value: 10.0},
+ {Value: 10.0},
+ },
+ },
+ expectAlert: false,
+ compareOp: "1", // Greater Than
+ matchType: "5", // Last
+ target: 20.0,
+ },
+ // less than last
+ {
+ values: v3.Series{
+ Points: []v3.Point{
+ {Value: 10.0},
+ {Value: 10.0},
+ },
+ },
+ expectAlert: true,
+ compareOp: "2", // Less Than
+ matchType: "5", // Last
+ target: 15.0,
+ expectedAlertSample: v3.Point{Value: 10.0},
+ },
+ {
+ values: v3.Series{
+ Points: []v3.Point{
+ {Value: 10.0},
+ {Value: 10.0},
+ },
+ },
+ expectAlert: false,
+ compareOp: "2", // Less Than
+ matchType: "5", // Last
+ target: 5.0,
+ },
+ // equals last
+ {
+ values: v3.Series{
+ Points: []v3.Point{
+ {Value: 10.0},
+ {Value: 10.0},
+ },
+ },
+ expectAlert: true,
+ compareOp: "3", // Equals
+ matchType: "5", // Last
+ target: 10.0,
+ expectedAlertSample: v3.Point{Value: 10.0},
+ },
+ {
+ values: v3.Series{
+ Points: []v3.Point{
+ {Value: 10.0},
+ {Value: 10.0},
+ },
+ },
+ expectAlert: false,
+ compareOp: "3", // Equals
+ matchType: "5", // Last
+ target: 5.0,
+ },
+ // not equals last
+ {
+ values: v3.Series{
+ Points: []v3.Point{
+ {Value: 10.0},
+ {Value: 10.0},
+ },
+ },
+ expectAlert: true,
+ compareOp: "4", // Not Equals
+ matchType: "5", // Last
+ target: 5.0,
+ expectedAlertSample: v3.Point{Value: 10.0},
+ },
+ {
+ values: v3.Series{
+ Points: []v3.Point{
+ {Value: 10.0},
+ {Value: 10.0},
+ },
+ },
+ expectAlert: false,
+ compareOp: "4", // Not Equals
+ matchType: "5", // Last
+ target: 10.0,
+ },
}
fm := featureManager.StartManager()