mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Move annotation functionality behind a history persistence interface (#56133)
* Move annotation functionality behind a history persistence interface * Rename to RecordState * Fix lint error in import aliasing * One more import linter error
This commit is contained in:
@@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/ngalert/schedule"
|
"github.com/grafana/grafana/pkg/services/ngalert/schedule"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/sender"
|
"github.com/grafana/grafana/pkg/services/ngalert/sender"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/state/historian"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
"github.com/grafana/grafana/pkg/services/notifications"
|
"github.com/grafana/grafana/pkg/services/notifications"
|
||||||
"github.com/grafana/grafana/pkg/services/quota"
|
"github.com/grafana/grafana/pkg/services/quota"
|
||||||
@@ -167,7 +168,8 @@ func (ng *AlertNG) init() error {
|
|||||||
AlertSender: alertsRouter,
|
AlertSender: alertsRouter,
|
||||||
}
|
}
|
||||||
|
|
||||||
stateManager := state.NewManager(ng.Log, ng.Metrics.GetStateMetrics(), appUrl, store, store, ng.dashboardService, ng.imageService, clk, ng.annotationsRepo)
|
historian := historian.NewAnnotationHistorian(ng.annotationsRepo, ng.dashboardService, ng.Log)
|
||||||
|
stateManager := state.NewManager(ng.Log, ng.Metrics.GetStateMetrics(), appUrl, store, store, ng.imageService, clk, historian)
|
||||||
scheduler := schedule.NewScheduler(schedCfg, appUrl, stateManager)
|
scheduler := schedule.NewScheduler(schedCfg, appUrl, stateManager)
|
||||||
|
|
||||||
// if it is required to include folder title to the alerts, we need to subscribe to changes of alert title
|
// if it is required to include folder title to the alerts, we need to subscribe to changes of alert title
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||||
@@ -112,7 +110,7 @@ func TestWarmStateCache(t *testing.T) {
|
|||||||
RuleStore: dbstore,
|
RuleStore: dbstore,
|
||||||
Metrics: testMetrics.GetSchedulerMetrics(),
|
Metrics: testMetrics.GetSchedulerMetrics(),
|
||||||
}
|
}
|
||||||
st := state.NewManager(schedCfg.Logger, testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.NewMock(), annotationstest.NewFakeAnnotationsRepo())
|
st := state.NewManager(schedCfg.Logger, testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.NewMock(), &state.FakeHistorian{})
|
||||||
st.Warm(ctx)
|
st.Warm(ctx)
|
||||||
|
|
||||||
t.Run("instance cache has expected entries", func(t *testing.T) {
|
t.Run("instance cache has expected entries", func(t *testing.T) {
|
||||||
@@ -165,7 +163,7 @@ func TestAlertingTicker(t *testing.T) {
|
|||||||
Metrics: testMetrics.GetSchedulerMetrics(),
|
Metrics: testMetrics.GetSchedulerMetrics(),
|
||||||
AlertSender: notifier,
|
AlertSender: notifier,
|
||||||
}
|
}
|
||||||
st := state.NewManager(schedCfg.Logger, testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.NewMock(), annotationstest.NewFakeAnnotationsRepo())
|
st := state.NewManager(schedCfg.Logger, testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.NewMock(), &state.FakeHistorian{})
|
||||||
appUrl := &url.URL{
|
appUrl := &url.URL{
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: "localhost",
|
Host: "localhost",
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/expr"
|
"github.com/grafana/grafana/pkg/expr"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
||||||
@@ -530,7 +528,7 @@ func setupScheduler(t *testing.T, rs *fakeRulesStore, is *state.FakeInstanceStor
|
|||||||
}
|
}
|
||||||
|
|
||||||
stateRs := state.FakeRuleReader{}
|
stateRs := state.FakeRuleReader{}
|
||||||
st := state.NewManager(schedCfg.Logger, m.GetStateMetrics(), nil, &stateRs, is, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, mockedClock, annotationstest.NewFakeAnnotationsRepo())
|
st := state.NewManager(schedCfg.Logger, m.GetStateMetrics(), nil, &stateRs, is, &image.NoopImageService{}, mockedClock, &state.FakeHistorian{})
|
||||||
return NewScheduler(schedCfg, appUrl, st)
|
return NewScheduler(schedCfg, appUrl, st)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
88
pkg/services/ngalert/state/historian/annotation.go
Normal file
88
pkg/services/ngalert/state/historian/annotation.go
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
package historian
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/annotations"
|
||||||
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnnotationStateHistorian is an implementation of state.Historian that uses Grafana Annotations as the backing datastore.
|
||||||
|
type AnnotationStateHistorian struct {
|
||||||
|
annotations annotations.Repository
|
||||||
|
dashboards dashboards.DashboardService
|
||||||
|
log log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAnnotationHistorian(annotations annotations.Repository, dashboards dashboards.DashboardService, log log.Logger) *AnnotationStateHistorian {
|
||||||
|
return &AnnotationStateHistorian{
|
||||||
|
annotations: annotations,
|
||||||
|
dashboards: dashboards,
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *AnnotationStateHistorian) RecordState(ctx context.Context, rule *ngmodels.AlertRule, labels data.Labels, evaluatedAt time.Time, currentData, previousData state.InstanceStateAndReason) {
|
||||||
|
h.log.Debug("alert state changed creating annotation", "alertRuleUID", rule.UID, "newState", currentData.String(), "oldState", previousData.String())
|
||||||
|
|
||||||
|
labels = removePrivateLabels(labels)
|
||||||
|
annotationText := fmt.Sprintf("%s {%s} - %s", rule.Title, labels.String(), currentData.String())
|
||||||
|
|
||||||
|
item := &annotations.Item{
|
||||||
|
AlertId: rule.ID,
|
||||||
|
OrgId: rule.OrgID,
|
||||||
|
PrevState: previousData.String(),
|
||||||
|
NewState: currentData.String(),
|
||||||
|
Text: annotationText,
|
||||||
|
Epoch: evaluatedAt.UnixNano() / int64(time.Millisecond),
|
||||||
|
}
|
||||||
|
|
||||||
|
dashUid, ok := rule.Annotations[ngmodels.DashboardUIDAnnotation]
|
||||||
|
if ok {
|
||||||
|
panelUid := rule.Annotations[ngmodels.PanelIDAnnotation]
|
||||||
|
|
||||||
|
panelId, err := strconv.ParseInt(panelUid, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
h.log.Error("error parsing panelUID for alert annotation", "panelUID", panelUid, "alertRuleUID", rule.UID, "err", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
query := &models.GetDashboardQuery{
|
||||||
|
Uid: dashUid,
|
||||||
|
OrgId: rule.OrgID,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.dashboards.GetDashboard(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
h.log.Error("error getting dashboard for alert annotation", "dashboardUID", dashUid, "alertRuleUID", rule.UID, "err", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item.PanelId = panelId
|
||||||
|
item.DashboardId = query.Result.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := h.annotations.Save(ctx, item); err != nil {
|
||||||
|
h.log.Error("error saving alert annotation", "alertRuleUID", rule.UID, "err", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func removePrivateLabels(labels data.Labels) data.Labels {
|
||||||
|
result := make(data.Labels)
|
||||||
|
for k, v := range labels {
|
||||||
|
if !strings.HasPrefix(k, "__") && !strings.HasSuffix(k, "__") {
|
||||||
|
result[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -5,17 +5,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/benbjohnson/clock"
|
"github.com/benbjohnson/clock"
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/models"
|
|
||||||
"github.com/grafana/grafana/pkg/services/annotations"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
"github.com/grafana/grafana/pkg/services/ngalert/image"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||||
@@ -41,28 +36,25 @@ type Manager struct {
|
|||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
ResendDelay time.Duration
|
ResendDelay time.Duration
|
||||||
|
|
||||||
ruleStore RuleReader
|
ruleStore RuleReader
|
||||||
instanceStore InstanceStore
|
instanceStore InstanceStore
|
||||||
dashboardService dashboards.DashboardService
|
imageService image.ImageService
|
||||||
imageService image.ImageService
|
historian Historian
|
||||||
AnnotationsRepo annotations.Repository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager(logger log.Logger, metrics *metrics.State, externalURL *url.URL,
|
func NewManager(logger log.Logger, metrics *metrics.State, externalURL *url.URL,
|
||||||
ruleStore RuleReader, instanceStore InstanceStore,
|
ruleStore RuleReader, instanceStore InstanceStore, imageService image.ImageService, clock clock.Clock, historian Historian) *Manager {
|
||||||
dashboardService dashboards.DashboardService, imageService image.ImageService, clock clock.Clock, annotationsRepo annotations.Repository) *Manager {
|
|
||||||
manager := &Manager{
|
manager := &Manager{
|
||||||
cache: newCache(logger, metrics, externalURL),
|
cache: newCache(logger, metrics, externalURL),
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
ResendDelay: ResendDelay, // TODO: make this configurable
|
ResendDelay: ResendDelay, // TODO: make this configurable
|
||||||
log: logger,
|
log: logger,
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
ruleStore: ruleStore,
|
ruleStore: ruleStore,
|
||||||
instanceStore: instanceStore,
|
instanceStore: instanceStore,
|
||||||
dashboardService: dashboardService,
|
imageService: imageService,
|
||||||
imageService: imageService,
|
historian: historian,
|
||||||
clock: clock,
|
clock: clock,
|
||||||
AnnotationsRepo: annotationsRepo,
|
|
||||||
}
|
}
|
||||||
go manager.recordMetrics()
|
go manager.recordMetrics()
|
||||||
return manager
|
return manager
|
||||||
@@ -281,7 +273,7 @@ func (st *Manager) setNextState(ctx context.Context, alertRule *ngModels.AlertRu
|
|||||||
|
|
||||||
shouldUpdateAnnotation := oldState != currentState.State || oldReason != currentState.StateReason
|
shouldUpdateAnnotation := oldState != currentState.State || oldReason != currentState.StateReason
|
||||||
if shouldUpdateAnnotation {
|
if shouldUpdateAnnotation {
|
||||||
go st.annotateState(ctx, alertRule, currentState.Labels, result.EvaluatedAt, InstanceStateAndReason{State: currentState.State, Reason: currentState.StateReason}, InstanceStateAndReason{State: oldState, Reason: oldReason})
|
go st.historian.RecordState(ctx, alertRule, currentState.Labels, result.EvaluatedAt, InstanceStateAndReason{State: currentState.State, Reason: currentState.StateReason}, InstanceStateAndReason{State: oldState, Reason: oldReason})
|
||||||
}
|
}
|
||||||
return currentState
|
return currentState
|
||||||
}
|
}
|
||||||
@@ -358,52 +350,6 @@ func (i InstanceStateAndReason) String() string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *Manager) annotateState(ctx context.Context, alertRule *ngModels.AlertRule, labels data.Labels, evaluatedAt time.Time, currentData, previousData InstanceStateAndReason) {
|
|
||||||
st.log.Debug("alert state changed creating annotation", "alertRuleUID", alertRule.UID, "newState", currentData.String(), "oldState", previousData.String())
|
|
||||||
|
|
||||||
labels = removePrivateLabels(labels)
|
|
||||||
annotationText := fmt.Sprintf("%s {%s} - %s", alertRule.Title, labels.String(), currentData.String())
|
|
||||||
|
|
||||||
item := &annotations.Item{
|
|
||||||
AlertId: alertRule.ID,
|
|
||||||
OrgId: alertRule.OrgID,
|
|
||||||
PrevState: previousData.String(),
|
|
||||||
NewState: currentData.String(),
|
|
||||||
Text: annotationText,
|
|
||||||
Epoch: evaluatedAt.UnixNano() / int64(time.Millisecond),
|
|
||||||
}
|
|
||||||
|
|
||||||
dashUid, ok := alertRule.Annotations[ngModels.DashboardUIDAnnotation]
|
|
||||||
if ok {
|
|
||||||
panelUid := alertRule.Annotations[ngModels.PanelIDAnnotation]
|
|
||||||
|
|
||||||
panelId, err := strconv.ParseInt(panelUid, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
st.log.Error("error parsing panelUID for alert annotation", "panelUID", panelUid, "alertRuleUID", alertRule.UID, "err", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
query := &models.GetDashboardQuery{
|
|
||||||
Uid: dashUid,
|
|
||||||
OrgId: alertRule.OrgID,
|
|
||||||
}
|
|
||||||
|
|
||||||
err = st.dashboardService.GetDashboard(ctx, query)
|
|
||||||
if err != nil {
|
|
||||||
st.log.Error("error getting dashboard for alert annotation", "dashboardUID", dashUid, "alertRuleUID", alertRule.UID, "err", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
item.PanelId = panelId
|
|
||||||
item.DashboardId = query.Result.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := st.AnnotationsRepo.Save(ctx, item); err != nil {
|
|
||||||
st.log.Error("error saving alert annotation", "alertRuleUID", alertRule.UID, "err", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Manager) staleResultsHandler(ctx context.Context, evaluatedAt time.Time, alertRule *ngModels.AlertRule, states map[string]*State) []*State {
|
func (st *Manager) staleResultsHandler(ctx context.Context, evaluatedAt time.Time, alertRule *ngModels.AlertRule, states map[string]*State) []*State {
|
||||||
var resolvedStates []*State
|
var resolvedStates []*State
|
||||||
allStates := st.GetStatesForRuleUID(alertRule.OrgID, alertRule.UID)
|
allStates := st.GetStatesForRuleUID(alertRule.OrgID, alertRule.UID)
|
||||||
@@ -428,7 +374,7 @@ func (st *Manager) staleResultsHandler(ctx context.Context, evaluatedAt time.Tim
|
|||||||
s.StateReason = ngModels.StateReasonMissingSeries
|
s.StateReason = ngModels.StateReasonMissingSeries
|
||||||
s.EndsAt = evaluatedAt
|
s.EndsAt = evaluatedAt
|
||||||
s.Resolved = true
|
s.Resolved = true
|
||||||
st.annotateState(ctx, alertRule, s.Labels, evaluatedAt,
|
st.historian.RecordState(ctx, alertRule, s.Labels, evaluatedAt,
|
||||||
InstanceStateAndReason{State: eval.Normal, Reason: s.StateReason},
|
InstanceStateAndReason{State: eval.Normal, Reason: s.StateReason},
|
||||||
previousState,
|
previousState,
|
||||||
)
|
)
|
||||||
@@ -442,13 +388,3 @@ func (st *Manager) staleResultsHandler(ctx context.Context, evaluatedAt time.Tim
|
|||||||
func isItStale(evaluatedAt time.Time, lastEval time.Time, intervalSeconds int64) bool {
|
func isItStale(evaluatedAt time.Time, lastEval time.Time, intervalSeconds int64) bool {
|
||||||
return !lastEval.Add(2 * time.Duration(intervalSeconds) * time.Second).After(evaluatedAt)
|
return !lastEval.Add(2 * time.Duration(intervalSeconds) * time.Second).After(evaluatedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removePrivateLabels(labels data.Labels) data.Labels {
|
|
||||||
result := make(data.Labels)
|
|
||||||
for k, v := range labels {
|
|
||||||
if !strings.HasPrefix(k, "__") && !strings.HasSuffix(k, "__") {
|
|
||||||
result[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import (
|
|||||||
"github.com/benbjohnson/clock"
|
"github.com/benbjohnson/clock"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/annotations/annotationstest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
@@ -95,7 +93,7 @@ func Test_maybeNewImage(t *testing.T) {
|
|||||||
imageService := &CountingImageService{}
|
imageService := &CountingImageService{}
|
||||||
mgr := NewManager(log.NewNopLogger(), &metrics.State{}, nil,
|
mgr := NewManager(log.NewNopLogger(), &metrics.State{}, nil,
|
||||||
&FakeRuleReader{}, &FakeInstanceStore{},
|
&FakeRuleReader{}, &FakeInstanceStore{},
|
||||||
&dashboards.FakeDashboardService{}, imageService, clock.NewMock(), annotationstest.NewFakeAnnotationsRepo())
|
imageService, clock.NewMock(), &FakeHistorian{})
|
||||||
err := mgr.maybeTakeScreenshot(context.Background(), &ngmodels.AlertRule{}, test.state, test.oldState)
|
err := mgr.maybeTakeScreenshot(context.Background(), &ngmodels.AlertRule{}, test.state, test.oldState)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
if !test.shouldScreenshot {
|
if !test.shouldScreenshot {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
"github.com/grafana/grafana/pkg/services/ngalert/metrics"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
||||||
|
"github.com/grafana/grafana/pkg/services/ngalert/state/historian"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/tests"
|
"github.com/grafana/grafana/pkg/services/ngalert/tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -38,7 +39,8 @@ func TestDashboardAnnotations(t *testing.T) {
|
|||||||
_, dbstore := tests.SetupTestEnv(t, 1)
|
_, dbstore := tests.SetupTestEnv(t, 1)
|
||||||
|
|
||||||
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
||||||
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.New(), fakeAnnoRepo)
|
hist := historian.NewAnnotationHistorian(fakeAnnoRepo, &dashboards.FakeDashboardService{}, log.NewNopLogger())
|
||||||
|
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.New(), hist)
|
||||||
|
|
||||||
const mainOrgID int64 = 1
|
const mainOrgID int64 = 1
|
||||||
|
|
||||||
@@ -1980,7 +1982,8 @@ func TestProcessEvalResults(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
fakeAnnoRepo := annotationstest.NewFakeAnnotationsRepo()
|
||||||
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, &state.FakeInstanceStore{}, &dashboards.FakeDashboardService{}, &image.NotAvailableImageService{}, clock.New(), fakeAnnoRepo)
|
hist := historian.NewAnnotationHistorian(fakeAnnoRepo, &dashboards.FakeDashboardService{}, log.NewNopLogger())
|
||||||
|
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, &state.FakeInstanceStore{}, &image.NotAvailableImageService{}, clock.New(), hist)
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(tc.desc, func(t *testing.T) {
|
||||||
for _, res := range tc.evalResults {
|
for _, res := range tc.evalResults {
|
||||||
_ = st.ProcessEvalResults(context.Background(), evaluationTime, tc.alertRule, res, data.Labels{
|
_ = st.ProcessEvalResults(context.Background(), evaluationTime, tc.alertRule, res, data.Labels{
|
||||||
@@ -2008,7 +2011,7 @@ func TestProcessEvalResults(t *testing.T) {
|
|||||||
t.Run("should save state to database", func(t *testing.T) {
|
t.Run("should save state to database", func(t *testing.T) {
|
||||||
instanceStore := &state.FakeInstanceStore{}
|
instanceStore := &state.FakeInstanceStore{}
|
||||||
clk := clock.New()
|
clk := clock.New()
|
||||||
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, instanceStore, &dashboards.FakeDashboardService{}, &image.NotAvailableImageService{}, clk, annotationstest.NewFakeAnnotationsRepo())
|
st := state.NewManager(log.New("test_state_manager"), testMetrics.GetStateMetrics(), nil, nil, instanceStore, &image.NotAvailableImageService{}, clk, &state.FakeHistorian{})
|
||||||
rule := models.AlertRuleGen()()
|
rule := models.AlertRuleGen()()
|
||||||
var results = eval.GenerateResults(rand.Intn(4)+1, eval.ResultGen(eval.WithEvaluatedAt(clk.Now())))
|
var results = eval.GenerateResults(rand.Intn(4)+1, eval.ResultGen(eval.WithEvaluatedAt(clk.Now())))
|
||||||
|
|
||||||
@@ -2125,7 +2128,7 @@ func TestStaleResultsHandler(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clock.New(), annotationstest.NewFakeAnnotationsRepo())
|
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clock.New(), &state.FakeHistorian{})
|
||||||
st.Warm(ctx)
|
st.Warm(ctx)
|
||||||
existingStatesForRule := st.GetStatesForRuleUID(rule.OrgID, rule.UID)
|
existingStatesForRule := st.GetStatesForRuleUID(rule.OrgID, rule.UID)
|
||||||
|
|
||||||
@@ -2188,7 +2191,7 @@ func TestStaleResults(t *testing.T) {
|
|||||||
clk := clock.NewMock()
|
clk := clock.NewMock()
|
||||||
clk.Set(time.Now())
|
clk.Set(time.Now())
|
||||||
|
|
||||||
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &dashboards.FakeDashboardService{}, &image.NoopImageService{}, clk, annotationstest.NewFakeAnnotationsRepo())
|
st := state.NewManager(log.New("test_stale_results_handler"), testMetrics.GetStateMetrics(), nil, dbstore, dbstore, &image.NoopImageService{}, clk, &state.FakeHistorian{})
|
||||||
|
|
||||||
orgID := rand.Int63()
|
orgID := rand.Int63()
|
||||||
rule := tests.CreateTestAlertRule(t, ctx, dbstore, 10, orgID)
|
rule := tests.CreateTestAlertRule(t, ctx, dbstore, 10, orgID)
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package state
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,3 +21,8 @@ type InstanceStore interface {
|
|||||||
type RuleReader interface {
|
type RuleReader interface {
|
||||||
ListAlertRules(ctx context.Context, query *models.ListAlertRulesQuery) error
|
ListAlertRules(ctx context.Context, query *models.ListAlertRulesQuery) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Historian maintains an audit log of alert state history.
|
||||||
|
type Historian interface {
|
||||||
|
RecordState(ctx context.Context, rule *models.AlertRule, labels data.Labels, evaluatedAt time.Time, currentData, previousData InstanceStateAndReason)
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ package state
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,3 +43,8 @@ type FakeRuleReader struct{}
|
|||||||
func (f *FakeRuleReader) ListAlertRules(_ context.Context, q *models.ListAlertRulesQuery) error {
|
func (f *FakeRuleReader) ListAlertRules(_ context.Context, q *models.ListAlertRulesQuery) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FakeHistorian struct{}
|
||||||
|
|
||||||
|
func (f *FakeHistorian) RecordState(ctx context.Context, rule *models.AlertRule, labels data.Labels, evaluatedAt time.Time, currentData, previousData InstanceStateAndReason) {
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user