Alerting: Allow importing Prometheus rules with keep_firing_for (#103557)
This commit is contained in:
@@ -196,6 +196,11 @@ func (p *Converter) convertRule(orgID int64, namespaceUID string, promGroup Prom
|
||||
forInterval = time.Duration(*rule.For)
|
||||
}
|
||||
|
||||
var keepFiringFor time.Duration
|
||||
if rule.KeepFiringFor != nil {
|
||||
keepFiringFor = time.Duration(*rule.KeepFiringFor)
|
||||
}
|
||||
|
||||
var query []models.AlertQuery
|
||||
var title string
|
||||
var isPaused bool
|
||||
@@ -236,19 +241,20 @@ func (p *Converter) convertRule(orgID int64, namespaceUID string, promGroup Prom
|
||||
}
|
||||
|
||||
result := models.AlertRule{
|
||||
OrgID: orgID,
|
||||
NamespaceUID: namespaceUID,
|
||||
Title: title,
|
||||
Data: query,
|
||||
Condition: query[len(query)-1].RefID,
|
||||
NoDataState: p.cfg.NoDataState,
|
||||
ExecErrState: p.cfg.ExecErrState,
|
||||
Annotations: rule.Annotations,
|
||||
Labels: labels,
|
||||
For: forInterval,
|
||||
RuleGroup: promGroup.Name,
|
||||
IsPaused: isPaused,
|
||||
Record: record,
|
||||
OrgID: orgID,
|
||||
NamespaceUID: namespaceUID,
|
||||
Title: title,
|
||||
Data: query,
|
||||
Condition: query[len(query)-1].RefID,
|
||||
NoDataState: p.cfg.NoDataState,
|
||||
ExecErrState: p.cfg.ExecErrState,
|
||||
Annotations: rule.Annotations,
|
||||
Labels: labels,
|
||||
For: forInterval,
|
||||
KeepFiringFor: keepFiringFor,
|
||||
RuleGroup: promGroup.Name,
|
||||
IsPaused: isPaused,
|
||||
Record: record,
|
||||
|
||||
// MissingSeriesEvalsToResolve is set to 1 to match the Prometheus behaviour.
|
||||
// Prometheus resolves alerts as soon as the series disappears.
|
||||
|
||||
@@ -41,9 +41,10 @@ func TestPrometheusRulesToGrafana(t *testing.T) {
|
||||
QueryOffset: util.Pointer(prommodel.Duration(1 * time.Minute)),
|
||||
Rules: []PrometheusRule{
|
||||
{
|
||||
Alert: "alert-1",
|
||||
Expr: "cpu_usage > 80",
|
||||
For: util.Pointer(prommodel.Duration(5 * time.Minute)),
|
||||
Alert: "alert-1",
|
||||
Expr: "cpu_usage > 80",
|
||||
For: util.Pointer(prommodel.Duration(5 * time.Minute)),
|
||||
KeepFiringFor: util.Pointer(prommodel.Duration(60 * time.Second)),
|
||||
Labels: map[string]string{
|
||||
"severity": "critical",
|
||||
},
|
||||
@@ -127,24 +128,6 @@ func TestPrometheusRulesToGrafana(t *testing.T) {
|
||||
expectError: true,
|
||||
errorMsg: "invalid target datasource type: non-prometheus-datasource, must be prometheus",
|
||||
},
|
||||
{
|
||||
name: "rules with keep_firing_for are not supported",
|
||||
orgID: 1,
|
||||
namespace: "namespaceUID",
|
||||
promGroup: PrometheusRuleGroup{
|
||||
Name: "test-group-1",
|
||||
Interval: prommodel.Duration(1 * time.Minute),
|
||||
Rules: []PrometheusRule{
|
||||
{
|
||||
Alert: "alert-1",
|
||||
Expr: "up == 0",
|
||||
KeepFiringFor: util.Pointer(prommodel.Duration(5 * time.Minute)),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "keep_firing_for is not supported",
|
||||
},
|
||||
{
|
||||
name: "rule group with empty interval",
|
||||
orgID: 1,
|
||||
@@ -330,6 +313,12 @@ func TestPrometheusRulesToGrafana(t *testing.T) {
|
||||
}
|
||||
require.Equal(t, expectedFor, grafanaRule.For, tc.name)
|
||||
|
||||
var expectedKeepFiringFor time.Duration
|
||||
if promRule.KeepFiringFor != nil {
|
||||
expectedKeepFiringFor = time.Duration(*promRule.KeepFiringFor)
|
||||
}
|
||||
require.Equal(t, expectedKeepFiringFor, grafanaRule.KeepFiringFor, tc.name)
|
||||
|
||||
expectedLabels := make(map[string]string, len(promRule.Labels)+len(tc.promGroup.Labels))
|
||||
maps.Copy(expectedLabels, tc.promGroup.Labels)
|
||||
maps.Copy(expectedLabels, promRule.Labels)
|
||||
|
||||
@@ -33,12 +33,6 @@ func (g *PrometheusRuleGroup) Validate() error {
|
||||
return ErrPrometheusRuleGroupValidationFailed.Errorf("query_offset must be >= 0")
|
||||
}
|
||||
|
||||
for _, rule := range g.Rules {
|
||||
if err := rule.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -51,11 +45,3 @@ type PrometheusRule struct {
|
||||
Annotations map[string]string `yaml:"annotations,omitempty"`
|
||||
Record string `yaml:"record,omitempty"`
|
||||
}
|
||||
|
||||
func (r *PrometheusRule) Validate() error {
|
||||
if r.KeepFiringFor != nil {
|
||||
return ErrPrometheusRuleValidationFailed.Errorf("keep_firing_for is not supported")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -29,8 +29,9 @@ func TestPrometheusRuleGroup_Validate(t *testing.T) {
|
||||
QueryOffset: util.Pointer(prommodel.Duration(time.Duration(1) * time.Second)),
|
||||
Rules: []PrometheusRule{
|
||||
{
|
||||
Alert: "test_alert",
|
||||
Expr: "up == 0",
|
||||
Alert: "test_alert",
|
||||
Expr: "up == 0",
|
||||
KeepFiringFor: util.Pointer(prommodel.Duration(10)),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -56,22 +57,6 @@ func TestPrometheusRuleGroup_Validate(t *testing.T) {
|
||||
expectError: true,
|
||||
errorMsg: "limit is not supported",
|
||||
},
|
||||
{
|
||||
name: "invalid group with invalid rule",
|
||||
group: PrometheusRuleGroup{
|
||||
Name: "test_group",
|
||||
Interval: prommodel.Duration(60),
|
||||
Rules: []PrometheusRule{
|
||||
{
|
||||
Alert: "test_alert",
|
||||
Expr: "up == 0",
|
||||
KeepFiringFor: util.Pointer(prommodel.Duration(10)),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "keep_firing_for is not supported",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -87,54 +72,6 @@ func TestPrometheusRuleGroup_Validate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusRule_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rule PrometheusRule
|
||||
expectError bool
|
||||
errorMsg string
|
||||
}{
|
||||
{
|
||||
name: "valid alert rule",
|
||||
rule: PrometheusRule{
|
||||
Alert: "test_alert",
|
||||
Expr: "up == 0",
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "valid recording rule",
|
||||
rule: PrometheusRule{
|
||||
Record: "test_recording",
|
||||
Expr: "sum(up)",
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "invalid rule with keep_firing_for",
|
||||
rule: PrometheusRule{
|
||||
Alert: "test_alert",
|
||||
Expr: "up == 0",
|
||||
KeepFiringFor: util.Pointer(prommodel.Duration(10)),
|
||||
},
|
||||
expectError: true,
|
||||
errorMsg: "keep_firing_for is not supported",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.rule.Validate()
|
||||
if tt.expectError {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tt.errorMsg)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusRulesFileYAML(t *testing.T) {
|
||||
interval := prommodel.Duration(5 * time.Minute)
|
||||
alertFor := prommodel.Duration(10 * time.Minute)
|
||||
|
||||
Reference in New Issue
Block a user