mirror of
https://github.com/grafana/grafana.git
synced 2025-02-12 00:25:46 -06:00
This change adds a field to state.State and models.AlertInstance that indicate the "Reason" that an instance has its current state. This helps us account for cases where the state is "Normal" but the underlying evaluation returned "NoData" or "Error", for example. Fixes #42606 Signed-off-by: Joe Blubaugh <joe.blubaugh@grafana.com>
161 lines
5.0 KiB
Go
161 lines
5.0 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
|
)
|
|
|
|
type InstanceStore interface {
|
|
GetAlertInstance(ctx context.Context, cmd *models.GetAlertInstanceQuery) error
|
|
ListAlertInstances(ctx context.Context, cmd *models.ListAlertInstancesQuery) error
|
|
SaveAlertInstance(ctx context.Context, cmd *models.SaveAlertInstanceCommand) error
|
|
FetchOrgIds(ctx context.Context) ([]int64, error)
|
|
DeleteAlertInstance(ctx context.Context, orgID int64, ruleUID, labelsHash string) error
|
|
}
|
|
|
|
// GetAlertInstance is a handler for retrieving an alert instance based on OrgId, AlertDefintionID, and
|
|
// the hash of the labels.
|
|
func (st DBstore) GetAlertInstance(ctx context.Context, cmd *models.GetAlertInstanceQuery) error {
|
|
return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
|
instance := models.AlertInstance{}
|
|
s := strings.Builder{}
|
|
s.WriteString(`SELECT * FROM alert_instance
|
|
WHERE
|
|
rule_org_id=? AND
|
|
rule_uid=? AND
|
|
labels_hash=?
|
|
`)
|
|
|
|
_, hash, err := cmd.Labels.StringAndHash()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
params := append(make([]interface{}, 0), cmd.RuleOrgID, cmd.RuleUID, hash)
|
|
|
|
has, err := sess.SQL(s.String(), params...).Get(&instance)
|
|
if !has {
|
|
return fmt.Errorf("instance not found for labels %v (hash: %v), alert rule %v (org %v)", cmd.Labels, hash, cmd.RuleUID, cmd.RuleOrgID)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cmd.Result = &instance
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// ListAlertInstances is a handler for retrieving alert instances within specific organisation
|
|
// based on various filters.
|
|
func (st DBstore) ListAlertInstances(ctx context.Context, cmd *models.ListAlertInstancesQuery) error {
|
|
return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
|
alertInstances := make([]*models.AlertInstance, 0)
|
|
|
|
s := strings.Builder{}
|
|
params := make([]interface{}, 0)
|
|
|
|
addToQuery := func(stmt string, p ...interface{}) {
|
|
s.WriteString(stmt)
|
|
params = append(params, p...)
|
|
}
|
|
|
|
addToQuery("SELECT alert_instance.*, alert_rule.title AS rule_title FROM alert_instance LEFT JOIN alert_rule ON alert_instance.rule_org_id = alert_rule.org_id AND alert_instance.rule_uid = alert_rule.uid WHERE rule_org_id = ?", cmd.RuleOrgID)
|
|
|
|
if cmd.RuleUID != "" {
|
|
addToQuery(` AND rule_uid = ?`, cmd.RuleUID)
|
|
}
|
|
|
|
if cmd.State != "" {
|
|
addToQuery(` AND current_state = ?`, cmd.State)
|
|
}
|
|
|
|
if cmd.StateReason != "" {
|
|
addToQuery(` AND current_reason = ?`, cmd.StateReason)
|
|
}
|
|
|
|
if err := sess.SQL(s.String(), params...).Find(&alertInstances); err != nil {
|
|
return err
|
|
}
|
|
|
|
cmd.Result = alertInstances
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// SaveAlertInstance is a handler for saving a new alert instance.
|
|
func (st DBstore) SaveAlertInstance(ctx context.Context, cmd *models.SaveAlertInstanceCommand) error {
|
|
return st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
|
labelTupleJSON, labelsHash, err := cmd.Labels.StringAndHash()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
alertInstance := &models.AlertInstance{
|
|
RuleOrgID: cmd.RuleOrgID,
|
|
RuleUID: cmd.RuleUID,
|
|
Labels: cmd.Labels,
|
|
LabelsHash: labelsHash,
|
|
CurrentState: cmd.State,
|
|
CurrentReason: cmd.StateReason,
|
|
CurrentStateSince: cmd.CurrentStateSince,
|
|
CurrentStateEnd: cmd.CurrentStateEnd,
|
|
LastEvalTime: cmd.LastEvalTime,
|
|
}
|
|
|
|
if err := models.ValidateAlertInstance(alertInstance); err != nil {
|
|
return err
|
|
}
|
|
|
|
params := append(make([]interface{}, 0), alertInstance.RuleOrgID, alertInstance.RuleUID, labelTupleJSON, alertInstance.LabelsHash, alertInstance.CurrentState, alertInstance.CurrentReason, alertInstance.CurrentStateSince.Unix(), alertInstance.CurrentStateEnd.Unix(), alertInstance.LastEvalTime.Unix())
|
|
|
|
upsertSQL := st.SQLStore.Dialect.UpsertSQL(
|
|
"alert_instance",
|
|
[]string{"rule_org_id", "rule_uid", "labels_hash"},
|
|
[]string{"rule_org_id", "rule_uid", "labels", "labels_hash", "current_state", "current_reason", "current_state_since", "current_state_end", "last_eval_time"})
|
|
_, err = sess.SQL(upsertSQL, params...).Query()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (st DBstore) FetchOrgIds(ctx context.Context) ([]int64, error) {
|
|
orgIds := []int64{}
|
|
|
|
err := st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
|
s := strings.Builder{}
|
|
params := make([]interface{}, 0)
|
|
|
|
addToQuery := func(stmt string, p ...interface{}) {
|
|
s.WriteString(stmt)
|
|
params = append(params, p...)
|
|
}
|
|
|
|
addToQuery("SELECT DISTINCT rule_org_id FROM alert_instance")
|
|
|
|
if err := sess.SQL(s.String(), params...).Find(&orgIds); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return orgIds, err
|
|
}
|
|
|
|
func (st DBstore) DeleteAlertInstance(ctx context.Context, orgID int64, ruleUID, labelsHash string) error {
|
|
return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
|
_, err := sess.Exec("DELETE FROM alert_instance WHERE rule_org_id = ? AND rule_uid = ? AND labels_hash = ?", orgID, ruleUID, labelsHash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
}
|