Alerting: Support hysteresis command expression (#75189)

Backend: 

* Update the Grafana Alerting engine to provide feedback to HysteresisCommand. The feedback information is stored in state.Manager as a fingerprint of each state. The fingerprint is persisted to the database. Only fingerprints that belong to Pending and Alerting states are considered as "loaded" and provided back to the command.
   - add ResultFingerprint to state.State. It's different from other fingerprints we store in the state because it is calculated from the result labels.
  -  add rule_fingerprint column to alert_instance
   - update alerting evaluator to accept AlertingResultsReader via context, and update scheduler to provide it.
   - add AlertingResultsFromRuleState that implements the new interface in eval package
   - update getExprRequest to patch the hysteresis command.

* Only one "Recovery Threshold" query is allowed to be used in the alert rule and it must be the Condition.


Frontend:

* Add hysteresis option to Threshold in UI. It's called "Recovery Threshold"
* Add test for getUnloadEvaluatorTypeFromCondition
* Hide hysteresis in panel expressions

* Refactor isInvalid and add test for it
* Remove unnecesary React.memo
* Add tests for updateEvaluatorConditions

---------

Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com>
This commit is contained in:
Yuri Tseretyan
2024-01-04 11:47:13 -05:00
committed by GitHub
co-authored by Sonia Aguilar
parent 29c251851d
commit f6a46744a6
33 changed files with 1804 additions and 201 deletions
@@ -145,7 +145,7 @@ func TestNewBacktestingEvaluator(t *testing.T) {
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
e, err := newBacktestingEvaluator(context.Background(), evalFactory, nil, testCase.condition)
e, err := newBacktestingEvaluator(context.Background(), evalFactory, nil, testCase.condition, nil)
if testCase.error {
require.Error(t, err)
return
@@ -175,7 +175,7 @@ func TestEvaluatorTest(t *testing.T) {
}
manager := &fakeStateManager{}
backtestingEvaluatorFactory = func(ctx context.Context, evalFactory eval.EvaluatorFactory, user identity.Requester, condition models.Condition) (backtestingEvaluator, error) {
backtestingEvaluatorFactory = func(ctx context.Context, evalFactory eval.EvaluatorFactory, user identity.Requester, condition models.Condition, r eval.AlertingResultsReader) (backtestingEvaluator, error) {
return evaluator, nil
}
@@ -386,6 +386,10 @@ func (f *fakeStateManager) ProcessEvalResults(_ context.Context, evaluatedAt tim
return f.stateCallback(evaluatedAt)
}
func (f *fakeStateManager) GetStatesForRuleUID(orgID int64, alertRuleUID string) []*state.State {
return nil
}
type fakeBacktestingEvaluator struct {
evalCallback func(now time.Time) (eval.Results, error)
}