From bd5bcea5d04074e2c93ba400ab9ec787c48345f7 Mon Sep 17 00:00:00 2001 From: Carl Bergquist Date: Mon, 20 May 2019 12:13:32 +0200 Subject: [PATCH] alerting: fix a bunch of lint issues. (#17128) --- pkg/services/alerting/engine.go | 33 +++++++++-------- .../alerting/engine_integration_test.go | 5 +-- pkg/services/alerting/engine_test.go | 8 +++-- pkg/services/alerting/eval_context.go | 16 ++++++--- pkg/services/alerting/eval_context_test.go | 4 +-- pkg/services/alerting/eval_handler.go | 3 ++ pkg/services/alerting/extractor.go | 8 ++--- pkg/services/alerting/extractor_test.go | 36 +++++++++---------- pkg/services/alerting/interfaces.go | 7 ++-- pkg/services/alerting/models.go | 9 +++-- pkg/services/alerting/notifier.go | 15 ++++---- pkg/services/alerting/reader.go | 31 ++++------------ pkg/services/alerting/result_handler.go | 18 +++++----- pkg/services/alerting/rule.go | 20 ++++++++--- pkg/services/alerting/scheduler.go | 12 +++---- pkg/services/alerting/test_notification.go | 4 ++- pkg/services/alerting/test_rule.go | 2 ++ pkg/services/alerting/ticker.go | 2 +- 18 files changed, 122 insertions(+), 111 deletions(-) diff --git a/pkg/services/alerting/engine.go b/pkg/services/alerting/engine.go index c67b59839d7..f2fd002704a 100644 --- a/pkg/services/alerting/engine.go +++ b/pkg/services/alerting/engine.go @@ -17,44 +17,43 @@ import ( "golang.org/x/sync/errgroup" ) +// AlertingService is the background process that +// schedules alert evaluations and makes sure notifications +// are sent. type AlertingService struct { RenderService rendering.Service `inject:""` - execQueue chan *Job - //clock clock.Clock + execQueue chan *Job ticker *Ticker - scheduler Scheduler - evalHandler EvalHandler - ruleReader RuleReader + scheduler scheduler + evalHandler evalHandler + ruleReader ruleReader log log.Logger - resultHandler ResultHandler + resultHandler resultHandler } func init() { registry.RegisterService(&AlertingService{}) } -func NewEngine() *AlertingService { - e := &AlertingService{} - e.Init() - return e -} - +// IsDisabled returns true if the alerting service is disable for this instance. func (e *AlertingService) IsDisabled() bool { return !setting.AlertingEnabled || !setting.ExecuteAlerts } +// Init initalizes the AlertingService. func (e *AlertingService) Init() error { e.ticker = NewTicker(time.Now(), time.Second*0, clock.New()) e.execQueue = make(chan *Job, 1000) - e.scheduler = NewScheduler() + e.scheduler = newScheduler() e.evalHandler = NewEvalHandler() - e.ruleReader = NewRuleReader() + e.ruleReader = newRuleReader() e.log = log.New("alerting.engine") - e.resultHandler = NewResultHandler(e.RenderService) + e.resultHandler = newResultHandler(e.RenderService) return nil } +// Run starts the alerting service background process. func (e *AlertingService) Run(ctx context.Context) error { alertGroup, ctx := errgroup.WithContext(ctx) alertGroup.Go(func() error { return e.alertingTicker(ctx) }) @@ -80,7 +79,7 @@ func (e *AlertingService) alertingTicker(grafanaCtx context.Context) error { case tick := <-e.ticker.C: // TEMP SOLUTION update rules ever tenth tick if tickIndex%10 == 0 { - e.scheduler.Update(e.ruleReader.Fetch()) + e.scheduler.Update(e.ruleReader.fetch()) } e.scheduler.Tick(tick, e.execQueue) @@ -211,7 +210,7 @@ func (e *AlertingService) processJob(attemptID int, attemptChan chan int, cancel // dont reuse the evalContext and get its own context. evalContext.Ctx = resultHandleCtx evalContext.Rule.State = evalContext.GetNewState() - e.resultHandler.Handle(evalContext) + e.resultHandler.handle(evalContext) span.Finish() e.log.Debug("Job Execution completed", "timeMs", evalContext.GetDurationMs(), "alertId", evalContext.Rule.Id, "name", evalContext.Rule.Name, "firing", evalContext.Firing, "attemptID", attemptID) close(attemptChan) diff --git a/pkg/services/alerting/engine_integration_test.go b/pkg/services/alerting/engine_integration_test.go index 3d54bdc3b4a..7d0d3360ad5 100644 --- a/pkg/services/alerting/engine_integration_test.go +++ b/pkg/services/alerting/engine_integration_test.go @@ -17,7 +17,8 @@ import ( func TestEngineTimeouts(t *testing.T) { Convey("Alerting engine timeout tests", t, func() { - engine := NewEngine() + engine := &AlertingService{} + engine.Init() setting.AlertingNotificationTimeout = 30 * time.Second setting.AlertingMaxAttempts = 3 engine.resultHandler = &FakeResultHandler{} @@ -89,7 +90,7 @@ func (handler *FakeCommonTimeoutHandler) Eval(evalContext *EvalContext) { evalContext.Error = errors.New("Fake evaluation timeout test failure; wrong response") } -func (handler *FakeCommonTimeoutHandler) Handle(evalContext *EvalContext) error { +func (handler *FakeCommonTimeoutHandler) handle(evalContext *EvalContext) error { // 1. prepare mock server path := "/resulthandle" srv := runBusyServer(path, handler.ServerBusySleepDuration) diff --git a/pkg/services/alerting/engine_test.go b/pkg/services/alerting/engine_test.go index 2e2ed0c9b16..4ed317d982f 100644 --- a/pkg/services/alerting/engine_test.go +++ b/pkg/services/alerting/engine_test.go @@ -6,9 +6,10 @@ import ( "math" "testing" + "time" + "github.com/grafana/grafana/pkg/setting" . "github.com/smartystreets/goconvey/convey" - "time" ) type FakeEvalHandler struct { @@ -32,13 +33,14 @@ func (handler *FakeEvalHandler) Eval(evalContext *EvalContext) { type FakeResultHandler struct{} -func (handler *FakeResultHandler) Handle(evalContext *EvalContext) error { +func (handler *FakeResultHandler) handle(evalContext *EvalContext) error { return nil } func TestEngineProcessJob(t *testing.T) { Convey("Alerting engine job processing", t, func() { - engine := NewEngine() + engine := &AlertingService{} + engine.Init() setting.AlertingEvaluationTimeout = 30 * time.Second setting.AlertingNotificationTimeout = 30 * time.Second setting.AlertingMaxAttempts = 3 diff --git a/pkg/services/alerting/eval_context.go b/pkg/services/alerting/eval_context.go index 7d9a9014086..480303fa6d7 100644 --- a/pkg/services/alerting/eval_context.go +++ b/pkg/services/alerting/eval_context.go @@ -11,6 +11,7 @@ import ( "github.com/grafana/grafana/pkg/setting" ) +// EvalContext is the context object for an alert evaluation. type EvalContext struct { Firing bool IsTestRun bool @@ -33,6 +34,7 @@ type EvalContext struct { Ctx context.Context } +// NewEvalContext is the EvalContext constructor. func NewEvalContext(alertCtx context.Context, rule *Rule) *EvalContext { return &EvalContext{ Ctx: alertCtx, @@ -45,12 +47,14 @@ func NewEvalContext(alertCtx context.Context, rule *Rule) *EvalContext { } } +// StateDescription contains visual information about the alert state. type StateDescription struct { Color string Text string Data string } +// GetStateModel returns the `StateDescription` based on current state. func (c *EvalContext) GetStateModel() *StateDescription { switch c.Rule.State { case models.AlertStateOK: @@ -78,18 +82,21 @@ func (c *EvalContext) GetStateModel() *StateDescription { } } -func (c *EvalContext) ShouldUpdateAlertState() bool { +func (c *EvalContext) shouldUpdateAlertState() bool { return c.Rule.State != c.PrevAlertState } -func (a *EvalContext) GetDurationMs() float64 { - return float64(a.EndTime.Nanosecond()-a.StartTime.Nanosecond()) / float64(1000000) +// GetDurationMs returns the duration of the alert evaluation. +func (c *EvalContext) GetDurationMs() float64 { + return float64(c.EndTime.Nanosecond()-c.StartTime.Nanosecond()) / float64(1000000) } +// GetNotificationTitle returns the title of the alert rule including alert state. func (c *EvalContext) GetNotificationTitle() string { return "[" + c.GetStateModel().Text + "] " + c.Rule.Name } +// GetDashboardUID returns the dashboard uid for the alert rule. func (c *EvalContext) GetDashboardUID() (*models.DashboardRef, error) { if c.dashboardRef != nil { return c.dashboardRef, nil @@ -106,6 +113,7 @@ func (c *EvalContext) GetDashboardUID() (*models.DashboardRef, error) { const urlFormat = "%s?fullscreen&edit&tab=alert&panelId=%d&orgId=%d" +// GetRuleUrl returns the url to the dashboard containing the alert. func (c *EvalContext) GetRuleUrl() (string, error) { if c.IsTestRun { return setting.AppUrl, nil @@ -118,7 +126,7 @@ func (c *EvalContext) GetRuleUrl() (string, error) { return fmt.Sprintf(urlFormat, models.GetFullDashboardUrl(ref.Uid, ref.Slug), c.Rule.PanelId, c.Rule.OrgId), nil } -// GetNewState returns the new state from the alert rule evaluation +// GetNewState returns the new state from the alert rule evaluation. func (c *EvalContext) GetNewState() models.AlertStateType { ns := getNewStateInternal(c) if ns != models.AlertStateAlerting || c.Rule.For == 0 { diff --git a/pkg/services/alerting/eval_context_test.go b/pkg/services/alerting/eval_context_test.go index 06d3ae16ed8..76cbc90c640 100644 --- a/pkg/services/alerting/eval_context_test.go +++ b/pkg/services/alerting/eval_context_test.go @@ -18,7 +18,7 @@ func TestStateIsUpdatedWhenNeeded(t *testing.T) { ctx.PrevAlertState = models.AlertStateOK ctx.Rule.State = models.AlertStateAlerting - if !ctx.ShouldUpdateAlertState() { + if !ctx.shouldUpdateAlertState() { t.Fatalf("expected should updated to be true") } }) @@ -27,7 +27,7 @@ func TestStateIsUpdatedWhenNeeded(t *testing.T) { ctx.PrevAlertState = models.AlertStateOK ctx.Rule.State = models.AlertStateOK - if ctx.ShouldUpdateAlertState() { + if ctx.shouldUpdateAlertState() { t.Fatalf("expected should updated to be false") } }) diff --git a/pkg/services/alerting/eval_handler.go b/pkg/services/alerting/eval_handler.go index 22d172568f5..572345362a8 100644 --- a/pkg/services/alerting/eval_handler.go +++ b/pkg/services/alerting/eval_handler.go @@ -9,11 +9,13 @@ import ( "github.com/grafana/grafana/pkg/infra/metrics" ) +// DefaultEvalHandler is responsible for evaluating the alert rule. type DefaultEvalHandler struct { log log.Logger alertJobTimeout time.Duration } +// NewEvalHandler is the `DefaultEvalHandler` constructor. func NewEvalHandler() *DefaultEvalHandler { return &DefaultEvalHandler{ log: log.New("alerting.evalHandler"), @@ -21,6 +23,7 @@ func NewEvalHandler() *DefaultEvalHandler { } } +// Eval evaluated the alert rule. func (e *DefaultEvalHandler) Eval(context *EvalContext) { firing := true noDataFound := true diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go index 6bf5e786c19..6fb109d71a2 100644 --- a/pkg/services/alerting/extractor.go +++ b/pkg/services/alerting/extractor.go @@ -11,7 +11,7 @@ import ( "github.com/grafana/grafana/pkg/models" ) -// DashAlertExtractor extracts alerts from the dashboard json +// DashAlertExtractor extracts alerts from the dashboard json. type DashAlertExtractor struct { User *models.SignedInUser Dash *models.Dashboard @@ -19,7 +19,7 @@ type DashAlertExtractor struct { log log.Logger } -// NewDashAlertExtractor returns a new DashAlertExtractor +// NewDashAlertExtractor returns a new DashAlertExtractor. func NewDashAlertExtractor(dash *models.Dashboard, orgID int64, user *models.SignedInUser) *DashAlertExtractor { return &DashAlertExtractor{ User: user, @@ -207,7 +207,7 @@ func validateAlertRule(alert *models.Alert) bool { return alert.ValidToSave() } -// GetAlerts extracts alerts from the dashboard json and does full validation on the alert json data +// GetAlerts extracts alerts from the dashboard json and does full validation on the alert json data. func (e *DashAlertExtractor) GetAlerts() ([]*models.Alert, error) { return e.extractAlerts(validateAlertRule) } @@ -247,7 +247,7 @@ func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *models.Alert } // ValidateAlerts validates alerts in the dashboard json but does not require a valid dashboard id -// in the first validation pass +// in the first validation pass. func (e *DashAlertExtractor) ValidateAlerts() error { _, err := e.extractAlerts(func(alert *models.Alert) bool { return alert.OrgId != 0 && alert.PanelId != 0 }) return err diff --git a/pkg/services/alerting/extractor_test.go b/pkg/services/alerting/extractor_test.go index 716ff746cd8..86f0725e4ce 100644 --- a/pkg/services/alerting/extractor_test.go +++ b/pkg/services/alerting/extractor_test.go @@ -52,10 +52,10 @@ func TestAlertRuleExtraction(t *testing.T) { So(err, ShouldBeNil) Convey("Extractor should not modify the original json", func() { - dashJson, err := simplejson.NewJson(json) + dashJSON, err := simplejson.NewJson(json) So(err, ShouldBeNil) - dash := models.NewDashboardFromJson(dashJson) + dash := models.NewDashboardFromJson(dashJSON) getTarget := func(j *simplejson.Json) string { rowObj := j.Get("rows").MustArray()[0] @@ -68,23 +68,23 @@ func TestAlertRuleExtraction(t *testing.T) { } Convey("Dashboard json rows.panels.alert.query.model.target should be empty", func() { - So(getTarget(dashJson), ShouldEqual, "") + So(getTarget(dashJSON), ShouldEqual, "") }) extractor := NewDashAlertExtractor(dash, 1, nil) _, _ = extractor.GetAlerts() Convey("Dashboard json should not be updated after extracting rules", func() { - So(getTarget(dashJson), ShouldEqual, "") + So(getTarget(dashJSON), ShouldEqual, "") }) }) Convey("Parsing and validating dashboard containing graphite alerts", func() { - dashJson, err := simplejson.NewJson(json) + dashJSON, err := simplejson.NewJson(json) So(err, ShouldBeNil) - dash := models.NewDashboardFromJson(dashJson) + dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) alerts, err := extractor.GetAlerts() @@ -147,12 +147,12 @@ func TestAlertRuleExtraction(t *testing.T) { }) Convey("Panels missing id should return error", func() { - panelWithoutId, err := ioutil.ReadFile("./testdata/panels-missing-id.json") + panelWithoutID, err := ioutil.ReadFile("./testdata/panels-missing-id.json") So(err, ShouldBeNil) - dashJson, err := simplejson.NewJson(panelWithoutId) + dashJSON, err := simplejson.NewJson(panelWithoutID) So(err, ShouldBeNil) - dash := models.NewDashboardFromJson(dashJson) + dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) _, err = extractor.GetAlerts() @@ -163,12 +163,12 @@ func TestAlertRuleExtraction(t *testing.T) { }) Convey("Panel with id set to zero should return error", func() { - panelWithIdZero, err := ioutil.ReadFile("./testdata/panel-with-id-0.json") + panelWithIDZero, err := ioutil.ReadFile("./testdata/panel-with-id-0.json") So(err, ShouldBeNil) - dashJson, err := simplejson.NewJson(panelWithIdZero) + dashJSON, err := simplejson.NewJson(panelWithIDZero) So(err, ShouldBeNil) - dash := models.NewDashboardFromJson(dashJson) + dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) _, err = extractor.GetAlerts() @@ -182,9 +182,9 @@ func TestAlertRuleExtraction(t *testing.T) { json, err := ioutil.ReadFile("./testdata/v5-dashboard.json") So(err, ShouldBeNil) - dashJson, err := simplejson.NewJson(json) + dashJSON, err := simplejson.NewJson(json) So(err, ShouldBeNil) - dash := models.NewDashboardFromJson(dashJson) + dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) alerts, err := extractor.GetAlerts() @@ -211,9 +211,9 @@ func TestAlertRuleExtraction(t *testing.T) { json, err := ioutil.ReadFile("./testdata/influxdb-alert.json") So(err, ShouldBeNil) - dashJson, err := simplejson.NewJson(json) + dashJSON, err := simplejson.NewJson(json) So(err, ShouldBeNil) - dash := models.NewDashboardFromJson(dashJson) + dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) alerts, err := extractor.GetAlerts() @@ -240,10 +240,10 @@ func TestAlertRuleExtraction(t *testing.T) { json, err := ioutil.ReadFile("./testdata/collapsed-panels.json") So(err, ShouldBeNil) - dashJson, err := simplejson.NewJson(json) + dashJSON, err := simplejson.NewJson(json) So(err, ShouldBeNil) - dash := models.NewDashboardFromJson(dashJson) + dash := models.NewDashboardFromJson(dashJSON) extractor := NewDashAlertExtractor(dash, 1, nil) alerts, err := extractor.GetAlerts() diff --git a/pkg/services/alerting/interfaces.go b/pkg/services/alerting/interfaces.go index bd7ca087769..be364d6f4cc 100644 --- a/pkg/services/alerting/interfaces.go +++ b/pkg/services/alerting/interfaces.go @@ -7,15 +7,16 @@ import ( "github.com/grafana/grafana/pkg/models" ) -type EvalHandler interface { +type evalHandler interface { Eval(evalContext *EvalContext) } -type Scheduler interface { +type scheduler interface { Tick(time time.Time, execQueue chan *Job) Update(rules []*Rule) } +// Notifier is responsible for sending alert notifications. type Notifier interface { Notify(evalContext *EvalContext) error GetType() string @@ -48,6 +49,7 @@ func (notifiers notifierStateSlice) ShouldUploadImage() bool { return false } +// ConditionResult is the result of a condition evaluation. type ConditionResult struct { Firing bool NoDataFound bool @@ -55,6 +57,7 @@ type ConditionResult struct { EvalMatches []*EvalMatch } +// Condition is responsible for evaluating an alert condition. type Condition interface { Eval(result *EvalContext) (*ConditionResult, error) } diff --git a/pkg/services/alerting/models.go b/pkg/services/alerting/models.go index bbd8b98eea2..e802ab5a7a1 100644 --- a/pkg/services/alerting/models.go +++ b/pkg/services/alerting/models.go @@ -2,6 +2,8 @@ package alerting import "github.com/grafana/grafana/pkg/components/null" +// Job holds state about when the alert rule should +// be evaluated. type Job struct { Offset int64 OffsetWait bool @@ -10,18 +12,15 @@ type Job struct { Rule *Rule } +// ResultLogEntry represents log data for the alert evaluation. type ResultLogEntry struct { Message string Data interface{} } +// EvalMatch represents the serie violating the threshold. type EvalMatch struct { Value null.Float `json:"value"` Metric string `json:"metric"` Tags map[string]string `json:"tags"` } - -type Level struct { - Operator string - Value float64 -} diff --git a/pkg/services/alerting/notifier.go b/pkg/services/alerting/notifier.go index a2824da4a67..de6d74239ae 100644 --- a/pkg/services/alerting/notifier.go +++ b/pkg/services/alerting/notifier.go @@ -13,6 +13,7 @@ import ( "github.com/grafana/grafana/pkg/setting" ) +// NotifierPlugin holds meta information about a notifier. type NotifierPlugin struct { Type string `json:"type"` Name string `json:"name"` @@ -21,11 +22,7 @@ type NotifierPlugin struct { Factory NotifierFactory `json:"-"` } -type NotificationService interface { - SendIfNeeded(context *EvalContext) error -} - -func NewNotificationService(renderService rendering.Service) NotificationService { +func newNotificationService(renderService rendering.Service) *notificationService { return ¬ificationService{ log: log.New("alerting.notifier"), renderService: renderService, @@ -156,8 +153,8 @@ func (n *notificationService) uploadImage(context *EvalContext) (err error) { return nil } -func (n *notificationService) getNeededNotifiers(orgId int64, notificationUids []string, evalContext *EvalContext) (notifierStateSlice, error) { - query := &models.GetAlertNotificationsWithUidToSendQuery{OrgId: orgId, Uids: notificationUids} +func (n *notificationService) getNeededNotifiers(orgID int64, notificationUids []string, evalContext *EvalContext) (notifierStateSlice, error) { + query := &models.GetAlertNotificationsWithUidToSendQuery{OrgId: orgID, Uids: notificationUids} if err := bus.Dispatch(query); err != nil { return nil, err @@ -194,7 +191,7 @@ func (n *notificationService) getNeededNotifiers(orgId int64, notificationUids [ return result, nil } -// InitNotifier instantiate a new notifier based on the model +// InitNotifier instantiate a new notifier based on the model. func InitNotifier(model *models.AlertNotification) (Notifier, error) { notifierPlugin, found := notifierFactories[model.Type] if !found { @@ -204,6 +201,7 @@ func InitNotifier(model *models.AlertNotification) (Notifier, error) { return notifierPlugin.Factory(model) } +// NotifierFactory is a signature for creating notifiers. type NotifierFactory func(notification *models.AlertNotification) (Notifier, error) var notifierFactories = make(map[string]*NotifierPlugin) @@ -213,6 +211,7 @@ func RegisterNotifier(plugin *NotifierPlugin) { notifierFactories[plugin.Type] = plugin } +// GetNotifiers returns a list of metadata about available notifiers. func GetNotifiers() []*NotifierPlugin { list := make([]*NotifierPlugin, 0) diff --git a/pkg/services/alerting/reader.go b/pkg/services/alerting/reader.go index 0df826e9ea5..c8020510ef6 100644 --- a/pkg/services/alerting/reader.go +++ b/pkg/services/alerting/reader.go @@ -2,7 +2,6 @@ package alerting import ( "sync" - "time" "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" @@ -10,35 +9,24 @@ import ( "github.com/grafana/grafana/pkg/models" ) -type RuleReader interface { - Fetch() []*Rule +type ruleReader interface { + fetch() []*Rule } -type DefaultRuleReader struct { +type defaultRuleReader struct { sync.RWMutex - serverPosition int - clusterSize int - log log.Logger + log log.Logger } -func NewRuleReader() *DefaultRuleReader { - ruleReader := &DefaultRuleReader{ +func newRuleReader() *defaultRuleReader { + ruleReader := &defaultRuleReader{ log: log.New("alerting.ruleReader"), } - go ruleReader.initReader() return ruleReader } -func (arr *DefaultRuleReader) initReader() { - heartbeat := time.NewTicker(time.Second * 10) - - for range heartbeat.C { - arr.heartbeat() - } -} - -func (arr *DefaultRuleReader) Fetch() []*Rule { +func (arr *defaultRuleReader) fetch() []*Rule { cmd := &models.GetAllAlertsQuery{} if err := bus.Dispatch(cmd); err != nil { @@ -58,8 +46,3 @@ func (arr *DefaultRuleReader) Fetch() []*Rule { metrics.M_Alerting_Active_Alerts.Set(float64(len(res))) return res } - -func (arr *DefaultRuleReader) heartbeat() { - arr.clusterSize = 1 - arr.serverPosition = 1 -} diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index d82421d7506..7141c6cec46 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -13,23 +13,23 @@ import ( "github.com/grafana/grafana/pkg/services/rendering" ) -type ResultHandler interface { - Handle(evalContext *EvalContext) error +type resultHandler interface { + handle(evalContext *EvalContext) error } -type DefaultResultHandler struct { - notifier NotificationService +type defaultResultHandler struct { + notifier *notificationService log log.Logger } -func NewResultHandler(renderService rendering.Service) *DefaultResultHandler { - return &DefaultResultHandler{ +func newResultHandler(renderService rendering.Service) *defaultResultHandler { + return &defaultResultHandler{ log: log.New("alerting.resultHandler"), - notifier: NewNotificationService(renderService), + notifier: newNotificationService(renderService), } } -func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { +func (handler *defaultResultHandler) handle(evalContext *EvalContext) error { executionError := "" annotationData := simplejson.New() @@ -45,7 +45,7 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { } metrics.M_Alerting_Result_State.WithLabelValues(string(evalContext.Rule.State)).Inc() - if evalContext.ShouldUpdateAlertState() { + if evalContext.shouldUpdateAlertState() { handler.log.Info("New state change", "alertId", evalContext.Rule.Id, "newState", evalContext.Rule.State, "prev state", evalContext.PrevAlertState) cmd := &models.SetAlertStateCommand{ diff --git a/pkg/services/alerting/rule.go b/pkg/services/alerting/rule.go index b5b6f4660e6..422148bc42f 100644 --- a/pkg/services/alerting/rule.go +++ b/pkg/services/alerting/rule.go @@ -12,10 +12,14 @@ import ( ) var ( + // ErrFrequencyCannotBeZeroOrLess frequency cannot be below zero ErrFrequencyCannotBeZeroOrLess = errors.New(`"evaluate every" cannot be zero or below`) - ErrFrequencyCouldNotBeParsed = errors.New(`"evaluate every" field could not be parsed`) + + // ErrFrequencyCouldNotBeParsed frequency cannot be parsed + ErrFrequencyCouldNotBeParsed = errors.New(`"evaluate every" field could not be parsed`) ) +// Rule is the in-memory version of an alert rule. type Rule struct { Id int64 OrgId int64 @@ -35,6 +39,8 @@ type Rule struct { StateChanges int64 } +// ValidationError is a typed error with meta data +// about the validation error. type ValidationError struct { Reason string Err error @@ -65,8 +71,8 @@ func (e ValidationError) Error() string { } var ( - ValueFormatRegex = regexp.MustCompile(`^\d+`) - UnitFormatRegex = regexp.MustCompile(`\w{1}$`) + valueFormatRegex = regexp.MustCompile(`^\d+`) + unitFormatRegex = regexp.MustCompile(`\w{1}$`) ) var unitMultiplier = map[string]int{ @@ -79,7 +85,7 @@ var unitMultiplier = map[string]int{ func getTimeDurationStringToSeconds(str string) (int64, error) { multiplier := 1 - matches := ValueFormatRegex.FindAllString(str, 1) + matches := valueFormatRegex.FindAllString(str, 1) if len(matches) <= 0 { return 0, ErrFrequencyCouldNotBeParsed @@ -94,7 +100,7 @@ func getTimeDurationStringToSeconds(str string) (int64, error) { return 0, ErrFrequencyCannotBeZeroOrLess } - unit := UnitFormatRegex.FindAllString(str, 1)[0] + unit := unitFormatRegex.FindAllString(str, 1)[0] if val, ok := unitMultiplier[unit]; ok { multiplier = val @@ -103,6 +109,8 @@ func getTimeDurationStringToSeconds(str string) (int64, error) { return int64(value * multiplier), nil } +// NewRuleFromDBAlert mappes an db version of +// alert to an in-memory version. func NewRuleFromDBAlert(ruleDef *models.Alert) (*Rule, error) { model := &Rule{} model.Id = ruleDef.Id @@ -159,10 +167,12 @@ func NewRuleFromDBAlert(ruleDef *models.Alert) (*Rule, error) { return model, nil } +// ConditionFactory is the function signature for creating `Conditions`. type ConditionFactory func(model *simplejson.Json, index int) (Condition, error) var conditionFactories = make(map[string]ConditionFactory) +// RegisterCondition adds support for alerting conditions. func RegisterCondition(typeName string, factory ConditionFactory) { conditionFactories[typeName] = factory } diff --git a/pkg/services/alerting/scheduler.go b/pkg/services/alerting/scheduler.go index 9a0769d25f2..62ef35298fc 100644 --- a/pkg/services/alerting/scheduler.go +++ b/pkg/services/alerting/scheduler.go @@ -8,19 +8,19 @@ import ( "github.com/grafana/grafana/pkg/models" ) -type SchedulerImpl struct { +type schedulerImpl struct { jobs map[int64]*Job log log.Logger } -func NewScheduler() Scheduler { - return &SchedulerImpl{ +func newScheduler() scheduler { + return &schedulerImpl{ jobs: make(map[int64]*Job), log: log.New("alerting.scheduler"), } } -func (s *SchedulerImpl) Update(rules []*Rule) { +func (s *schedulerImpl) Update(rules []*Rule) { s.log.Debug("Scheduling update", "ruleCount", len(rules)) jobs := make(map[int64]*Job) @@ -48,7 +48,7 @@ func (s *SchedulerImpl) Update(rules []*Rule) { s.jobs = jobs } -func (s *SchedulerImpl) Tick(tickTime time.Time, execQueue chan *Job) { +func (s *schedulerImpl) Tick(tickTime time.Time, execQueue chan *Job) { now := tickTime.Unix() for _, job := range s.jobs { @@ -72,7 +72,7 @@ func (s *SchedulerImpl) Tick(tickTime time.Time, execQueue chan *Job) { } } -func (s *SchedulerImpl) enqueue(job *Job, execQueue chan *Job) { +func (s *schedulerImpl) enqueue(job *Job, execQueue chan *Job) { s.log.Debug("Scheduler: Putting job on to exec queue", "name", job.Rule.Name, "id", job.Rule.Id) execQueue <- job } diff --git a/pkg/services/alerting/test_notification.go b/pkg/services/alerting/test_notification.go index 5cb18d2b42e..3651fffa68b 100644 --- a/pkg/services/alerting/test_notification.go +++ b/pkg/services/alerting/test_notification.go @@ -11,6 +11,8 @@ import ( "github.com/grafana/grafana/pkg/models" ) +// NotificationTestCommand initiates an test +// execution of an alert notification. type NotificationTestCommand struct { State models.AlertStateType Name string @@ -27,7 +29,7 @@ func init() { } func handleNotificationTestCommand(cmd *NotificationTestCommand) error { - notifier := NewNotificationService(nil).(*notificationService) + notifier := newNotificationService(nil) model := &models.AlertNotification{ Name: cmd.Name, diff --git a/pkg/services/alerting/test_rule.go b/pkg/services/alerting/test_rule.go index 736dd287dbe..7a44845614b 100644 --- a/pkg/services/alerting/test_rule.go +++ b/pkg/services/alerting/test_rule.go @@ -9,6 +9,8 @@ import ( "github.com/grafana/grafana/pkg/models" ) +// AlertTestCommand initiates an test evaluation +// of an alert rule. type AlertTestCommand struct { Dashboard *simplejson.Json PanelId int64 diff --git a/pkg/services/alerting/ticker.go b/pkg/services/alerting/ticker.go index 8cee2653ee9..9702a2cda63 100644 --- a/pkg/services/alerting/ticker.go +++ b/pkg/services/alerting/ticker.go @@ -6,7 +6,7 @@ import ( "github.com/benbjohnson/clock" ) -// ticker is a ticker to power the alerting scheduler. it's like a time.Ticker, except: +// Ticker is a ticker to power the alerting scheduler. it's like a time.Ticker, except: // * it doesn't drop ticks for slow receivers, rather, it queues up. so that callers are in control to instrument what's going on. // * it automatically ticks every second, which is the right thing in our current design // * it ticks on second marks or very shortly after. this provides a predictable load pattern