From fb6aa0e026c8e1b903be5eef11141eb08c72651e Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 15 Dec 2016 17:01:45 +0100 Subject: [PATCH 1/8] feat(api): adds endpoint for mass pausing alerts ref #6589 --- pkg/api/alerting.go | 72 +++++++++++++++++-- pkg/api/alerting_test.go | 11 +++ pkg/api/api.go | 1 + pkg/api/dtos/alerting.go | 5 ++ pkg/models/alert.go | 7 +- pkg/services/alerting/conditions/query.go | 4 ++ pkg/services/alerting/interfaces.go | 1 + pkg/services/sqlstore/alert.go | 37 ++++++---- pkg/services/sqlstore/alert_heartbeat_test.go | 18 ----- 9 files changed, 117 insertions(+), 39 deletions(-) create mode 100644 pkg/api/alerting_test.go delete mode 100644 pkg/services/sqlstore/alert_heartbeat_test.go diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index dcc9b40b07a..3cad65d029d 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -259,10 +259,11 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R //POST /api/alerts/:alertId/pause func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { + alertId := c.ParamsInt64("alertId") cmd := models.PauseAlertCommand{ - OrgId: c.OrgId, - AlertId: c.ParamsInt64("alertId"), - Paused: dto.Paused, + OrgId: c.OrgId, + AlertIds: []int64{alertId}, + Paused: dto.Paused, } if err := bus.Dispatch(&cmd); err != nil { @@ -277,10 +278,73 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { } result := map[string]interface{}{ - "alertId": cmd.AlertId, + "alertId": alertId, "state": response, "message": "alert " + pausedState, } return Json(200, result) } + +func existInSlice(slice []int64, value int64) bool { + for _, v := range slice { + if v == value { + return true + } + } + return false +} + +//POST /api/alerts/pause +func PauseAlerts(c *middleware.Context, dto dtos.PauseAlertsCommand) Response { + cmd := &models.GetAllAlertsQuery{} + if err := bus.Dispatch(cmd); err != nil { + return ApiError(500, "", err) + } + + var alertsToUpdate []int64 + skipFilter := len(dto.DataSourceIds) == 0 + for _, alert := range cmd.Result { + if skipFilter { + alertsToUpdate = append(alertsToUpdate, alert.Id) + continue + } + + alert, err := alerting.NewRuleFromDBAlert(alert) + if err != nil { + return ApiError(500, "", err) + } + + for _, v := range alert.Conditions { + id, exist := v.GetDatsourceId() + if exist && existInSlice(dto.DataSourceIds, *id) { + alertsToUpdate = append(alertsToUpdate, alert.Id) + } + } + } + + updateCmd := models.PauseAlertCommand{ + OrgId: c.OrgId, + AlertIds: alertsToUpdate, + Paused: dto.Paused, + } + + if err := bus.Dispatch(&updateCmd); err != nil { + return ApiError(500, "", err) + } + + var response models.AlertStateType = models.AlertStatePending + pausedState := "un paused" + if updateCmd.Paused { + response = models.AlertStatePaused + pausedState = "paused" + } + + result := map[string]interface{}{ + "state": response, + "message": "alert " + pausedState, + "alertsAffected": updateCmd.ResultCount, + } + + return Json(200, result) +} diff --git a/pkg/api/alerting_test.go b/pkg/api/alerting_test.go new file mode 100644 index 00000000000..395f4bbb671 --- /dev/null +++ b/pkg/api/alerting_test.go @@ -0,0 +1,11 @@ +package api + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestAlertingApi(t *testing.T) { + Convey("", func() {}) +} diff --git a/pkg/api/api.go b/pkg/api/api.go index 90e0e1bdfa8..f7ec069b04e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -256,6 +256,7 @@ func Register(r *macaron.Macaron) { r.Group("/alerts", func() { r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest)) r.Post("/:alertId/pause", bind(dtos.PauseAlertCommand{}), wrap(PauseAlert), reqEditorRole) + r.Post("/pause", bind(dtos.PauseAlertsCommand{}), wrap(PauseAlerts), reqGrafanaAdmin) r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert)) r.Get("/", wrap(GetAlerts)) r.Get("/states-for-dashboard", wrap(GetAlertStatesForDashboard)) diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index c9ba30e2407..c990b347aa3 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -64,3 +64,8 @@ type PauseAlertCommand struct { AlertId int64 `json:"alertId"` Paused bool `json:"paused"` } + +type PauseAlertsCommand struct { + DataSourceIds []int64 `json:"datasourceId"` + Paused bool `json:"paused"` +} diff --git a/pkg/models/alert.go b/pkg/models/alert.go index 0d2289c0678..3f7a5fb9c3f 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -132,9 +132,10 @@ type SaveAlertsCommand struct { } type PauseAlertCommand struct { - OrgId int64 - AlertId int64 - Paused bool + OrgId int64 + AlertIds []int64 + ResultCount int64 + Paused bool } type SetAlertStateCommand struct { diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go index d7e0a0c29e3..db3a615f924 100644 --- a/pkg/services/alerting/conditions/query.go +++ b/pkg/services/alerting/conditions/query.go @@ -34,6 +34,10 @@ type AlertQuery struct { To string } +func (c *QueryCondition) GetDatsourceId() (datasourceId *int64, exist bool) { + return &c.Query.DatasourceId, true +} + func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) { timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To) diff --git a/pkg/services/alerting/interfaces.go b/pkg/services/alerting/interfaces.go index 566fbdb2898..bcc44fcf52b 100644 --- a/pkg/services/alerting/interfaces.go +++ b/pkg/services/alerting/interfaces.go @@ -30,4 +30,5 @@ type ConditionResult struct { type Condition interface { Eval(result *EvalContext) (*ConditionResult, error) + GetDatsourceId() (datasourceId *int64, exist bool) } diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 44693f8d474..0f9f78eabb9 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -5,6 +5,8 @@ import ( "fmt" "time" + "strings" + "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" @@ -246,25 +248,32 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error { func PauseAlertRule(cmd *m.PauseAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { - alert := m.Alert{} + var buffer bytes.Buffer + params := make([]interface{}, 0) + buffer.WriteString(`UPDATE alert SET state = ?`) - has, err := x.Where("id = ? AND org_id=?", cmd.AlertId, cmd.OrgId).Get(&alert) + alertIdCount := len(cmd.AlertIds) + if alertIdCount == 1 { + buffer.WriteString(` WHERE id = ?`) + } else if alertIdCount > 1 { + buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`) + } + if cmd.Paused { + params = append(params, string(m.AlertStatePaused)) + } else { + params = append(params, string(m.AlertStatePending)) + } + + for _, v := range cmd.AlertIds { + params = append(params, v) + } + + res, err := sess.Exec(buffer.String(), params...) if err != nil { return err - } else if !has { - return fmt.Errorf("Could not find alert") } - - var newState m.AlertStateType - if cmd.Paused { - newState = m.AlertStatePaused - } else { - newState = m.AlertStatePending - } - alert.State = newState - - sess.Id(alert.Id).Update(&alert) + cmd.ResultCount, _ = res.RowsAffected() return nil }) } diff --git a/pkg/services/sqlstore/alert_heartbeat_test.go b/pkg/services/sqlstore/alert_heartbeat_test.go deleted file mode 100644 index 196d3fd7dde..00000000000 --- a/pkg/services/sqlstore/alert_heartbeat_test.go +++ /dev/null @@ -1,18 +0,0 @@ -package sqlstore - -import ( - "testing" - - // m "github.com/grafana/grafana/pkg/models" - . "github.com/smartystreets/goconvey/convey" -) - -func TestAlertingHeartbeatDataAccess(t *testing.T) { - - Convey("Testing Alerting data access", t, func() { - InitTestDB(t) - //send heartbeat from server 1 - //send heartbeat from server 2 - - }) -} From 1f8f3ed038cdb2479293148df4c55488d30e710a Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 16 Dec 2016 15:19:13 +0100 Subject: [PATCH 2/8] style(api): extracts filter logic for mass pause --- pkg/api/alerting.go | 79 +++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 3cad65d029d..46a3e008f11 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -286,51 +286,21 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { return Json(200, result) } -func existInSlice(slice []int64, value int64) bool { - for _, v := range slice { - if v == value { - return true - } - } - return false -} - //POST /api/alerts/pause func PauseAlerts(c *middleware.Context, dto dtos.PauseAlertsCommand) Response { - cmd := &models.GetAllAlertsQuery{} - if err := bus.Dispatch(cmd); err != nil { - return ApiError(500, "", err) - } - - var alertsToUpdate []int64 - skipFilter := len(dto.DataSourceIds) == 0 - for _, alert := range cmd.Result { - if skipFilter { - alertsToUpdate = append(alertsToUpdate, alert.Id) - continue - } - - alert, err := alerting.NewRuleFromDBAlert(alert) - if err != nil { - return ApiError(500, "", err) - } - - for _, v := range alert.Conditions { - id, exist := v.GetDatsourceId() - if exist && existInSlice(dto.DataSourceIds, *id) { - alertsToUpdate = append(alertsToUpdate, alert.Id) - } - } + alertIdsToUpdate, err := getAlertIdsToUpdate(dto) + if err != nil { + return ApiError(500, "Failed to pause alerts", err) } updateCmd := models.PauseAlertCommand{ OrgId: c.OrgId, - AlertIds: alertsToUpdate, + AlertIds: alertIdsToUpdate, Paused: dto.Paused, } if err := bus.Dispatch(&updateCmd); err != nil { - return ApiError(500, "", err) + return ApiError(500, "Failed to pause alerts", err) } var response models.AlertStateType = models.AlertStatePending @@ -348,3 +318,42 @@ func PauseAlerts(c *middleware.Context, dto dtos.PauseAlertsCommand) Response { return Json(200, result) } + +func getAlertIdsToUpdate(pauseAlertCmd dtos.PauseAlertsCommand) ([]int64, error) { + cmd := &models.GetAllAlertsQuery{} + if err := bus.Dispatch(cmd); err != nil { + return nil, err + } + + var alertIdsToUpdate []int64 + updateAllAlerts := len(pauseAlertCmd.DataSourceIds) == 0 + for _, alert := range cmd.Result { + if updateAllAlerts { + alertIdsToUpdate = append(alertIdsToUpdate, alert.Id) + continue + } + + alert, err := alerting.NewRuleFromDBAlert(alert) + if err != nil { + return nil, err + } + + for _, condition := range alert.Conditions { + id, exist := condition.GetDatsourceId() + if exist && existInSlice(pauseAlertCmd.DataSourceIds, *id) { + alertIdsToUpdate = append(alertIdsToUpdate, alert.Id) + } + } + } + + return alertIdsToUpdate, nil +} + +func existInSlice(slice []int64, value int64) bool { + for _, v := range slice { + if v == value { + return true + } + } + return false +} From 4ef95cfd31e150342930c1231dc2830e87a9e517 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 16 Dec 2016 16:07:23 +0100 Subject: [PATCH 3/8] style(api): reduce code complexity --- pkg/api/alerting.go | 24 +++++++++------------- pkg/api/alerting_test.go | 11 ---------- pkg/services/alerting/conditions/query.go | 2 +- pkg/services/alerting/eval_handler_test.go | 4 ++++ pkg/services/alerting/interfaces.go | 2 +- pkg/services/alerting/rule_test.go | 4 ++++ 6 files changed, 20 insertions(+), 27 deletions(-) delete mode 100644 pkg/api/alerting_test.go diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 46a3e008f11..d84151e5f45 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -288,15 +288,17 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { //POST /api/alerts/pause func PauseAlerts(c *middleware.Context, dto dtos.PauseAlertsCommand) Response { - alertIdsToUpdate, err := getAlertIdsToUpdate(dto) - if err != nil { - return ApiError(500, "Failed to pause alerts", err) + updateCmd := models.PauseAlertCommand{ + OrgId: c.OrgId, + Paused: dto.Paused, } - updateCmd := models.PauseAlertCommand{ - OrgId: c.OrgId, - AlertIds: alertIdsToUpdate, - Paused: dto.Paused, + if len(dto.DataSourceIds) > 0 { + alertIdsToUpdate, err := getAlertIdsToUpdate(dto) + if err != nil { + return ApiError(500, "Failed to pause alerts", err) + } + updateCmd.AlertIds = alertIdsToUpdate } if err := bus.Dispatch(&updateCmd); err != nil { @@ -326,20 +328,14 @@ func getAlertIdsToUpdate(pauseAlertCmd dtos.PauseAlertsCommand) ([]int64, error) } var alertIdsToUpdate []int64 - updateAllAlerts := len(pauseAlertCmd.DataSourceIds) == 0 for _, alert := range cmd.Result { - if updateAllAlerts { - alertIdsToUpdate = append(alertIdsToUpdate, alert.Id) - continue - } - alert, err := alerting.NewRuleFromDBAlert(alert) if err != nil { return nil, err } for _, condition := range alert.Conditions { - id, exist := condition.GetDatsourceId() + id, exist := condition.GetDatasourceId() if exist && existInSlice(pauseAlertCmd.DataSourceIds, *id) { alertIdsToUpdate = append(alertIdsToUpdate, alert.Id) } diff --git a/pkg/api/alerting_test.go b/pkg/api/alerting_test.go deleted file mode 100644 index 395f4bbb671..00000000000 --- a/pkg/api/alerting_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package api - -import ( - "testing" - - . "github.com/smartystreets/goconvey/convey" -) - -func TestAlertingApi(t *testing.T) { - Convey("", func() {}) -} diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go index db3a615f924..ca6c5f8b5d5 100644 --- a/pkg/services/alerting/conditions/query.go +++ b/pkg/services/alerting/conditions/query.go @@ -34,7 +34,7 @@ type AlertQuery struct { To string } -func (c *QueryCondition) GetDatsourceId() (datasourceId *int64, exist bool) { +func (c *QueryCondition) GetDatasourceId() (datasourceId *int64, exist bool) { return &c.Query.DatasourceId, true } diff --git a/pkg/services/alerting/eval_handler_test.go b/pkg/services/alerting/eval_handler_test.go index 73d675e3fa7..fd0d73f58b9 100644 --- a/pkg/services/alerting/eval_handler_test.go +++ b/pkg/services/alerting/eval_handler_test.go @@ -18,6 +18,10 @@ func (c *conditionStub) Eval(context *EvalContext) (*ConditionResult, error) { return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator, NoDataFound: c.noData}, nil } +func (c *conditionStub) GetDatasourceId() (datasourceId *int64, exist bool) { + return nil, false +} + func TestAlertingExecutor(t *testing.T) { Convey("Test alert execution", t, func() { handler := NewEvalHandler() diff --git a/pkg/services/alerting/interfaces.go b/pkg/services/alerting/interfaces.go index bcc44fcf52b..32c78b1dd62 100644 --- a/pkg/services/alerting/interfaces.go +++ b/pkg/services/alerting/interfaces.go @@ -30,5 +30,5 @@ type ConditionResult struct { type Condition interface { Eval(result *EvalContext) (*ConditionResult, error) - GetDatsourceId() (datasourceId *int64, exist bool) + GetDatasourceId() (datasourceId *int64, exist bool) } diff --git a/pkg/services/alerting/rule_test.go b/pkg/services/alerting/rule_test.go index f8761efcd90..426712fa9f2 100644 --- a/pkg/services/alerting/rule_test.go +++ b/pkg/services/alerting/rule_test.go @@ -14,6 +14,10 @@ func (f *FakeCondition) Eval(context *EvalContext) (*ConditionResult, error) { return &ConditionResult{}, nil } +func (c *FakeCondition) GetDatasourceId() (datasourceId *int64, exist bool) { + return nil, false +} + func TestAlertRuleModel(t *testing.T) { Convey("Testing alert rule", t, func() { From 6b9db0c0c5ce97e0d91b78b1ccce59cb95272792 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 19 Dec 2016 08:18:42 +0100 Subject: [PATCH 4/8] style(alerting): simplify support for pausing multiple alerts --- pkg/services/sqlstore/alert.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 0f9f78eabb9..819b5cf544d 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -250,23 +250,19 @@ func PauseAlertRule(cmd *m.PauseAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { var buffer bytes.Buffer params := make([]interface{}, 0) + buffer.WriteString(`UPDATE alert SET state = ?`) - - alertIdCount := len(cmd.AlertIds) - if alertIdCount == 1 { - buffer.WriteString(` WHERE id = ?`) - } else if alertIdCount > 1 { - buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`) - } - if cmd.Paused { params = append(params, string(m.AlertStatePaused)) } else { params = append(params, string(m.AlertStatePending)) } - for _, v := range cmd.AlertIds { - params = append(params, v) + if len(cmd.AlertIds) > 0 { + buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`) + for _, v := range cmd.AlertIds { + params = append(params, v) + } } res, err := sess.Exec(buffer.String(), params...) From ecdf1888c4caf87be052ed113ba072b24a415d0c Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 19 Dec 2016 13:24:45 +0100 Subject: [PATCH 5/8] feat(alerting): removes pause per datasource --- pkg/api/alerting.go | 46 +---------------------- pkg/api/api.go | 2 +- pkg/api/dtos/alerting.go | 5 +-- pkg/metrics/metrics.go | 2 +- pkg/models/alert.go | 11 ++++++ pkg/services/alerting/conditions/query.go | 4 -- pkg/services/alerting/interfaces.go | 1 - pkg/services/alerting/result_handler.go | 4 ++ pkg/services/alerting/rule_test.go | 4 -- pkg/services/sqlstore/alert.go | 38 +++++++++++++++---- pkg/services/sqlstore/alert_test.go | 31 +++++++++++++++ 11 files changed, 83 insertions(+), 65 deletions(-) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index d84151e5f45..f783e6e4229 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -287,20 +287,11 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { } //POST /api/alerts/pause -func PauseAlerts(c *middleware.Context, dto dtos.PauseAlertsCommand) Response { - updateCmd := models.PauseAlertCommand{ - OrgId: c.OrgId, +func PauseAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Response { + updateCmd := models.PauseAllAlertCommand{ Paused: dto.Paused, } - if len(dto.DataSourceIds) > 0 { - alertIdsToUpdate, err := getAlertIdsToUpdate(dto) - if err != nil { - return ApiError(500, "Failed to pause alerts", err) - } - updateCmd.AlertIds = alertIdsToUpdate - } - if err := bus.Dispatch(&updateCmd); err != nil { return ApiError(500, "Failed to pause alerts", err) } @@ -320,36 +311,3 @@ func PauseAlerts(c *middleware.Context, dto dtos.PauseAlertsCommand) Response { return Json(200, result) } - -func getAlertIdsToUpdate(pauseAlertCmd dtos.PauseAlertsCommand) ([]int64, error) { - cmd := &models.GetAllAlertsQuery{} - if err := bus.Dispatch(cmd); err != nil { - return nil, err - } - - var alertIdsToUpdate []int64 - for _, alert := range cmd.Result { - alert, err := alerting.NewRuleFromDBAlert(alert) - if err != nil { - return nil, err - } - - for _, condition := range alert.Conditions { - id, exist := condition.GetDatasourceId() - if exist && existInSlice(pauseAlertCmd.DataSourceIds, *id) { - alertIdsToUpdate = append(alertIdsToUpdate, alert.Id) - } - } - } - - return alertIdsToUpdate, nil -} - -func existInSlice(slice []int64, value int64) bool { - for _, v := range slice { - if v == value { - return true - } - } - return false -} diff --git a/pkg/api/api.go b/pkg/api/api.go index f7ec069b04e..4b142be1b1f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -256,7 +256,6 @@ func Register(r *macaron.Macaron) { r.Group("/alerts", func() { r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest)) r.Post("/:alertId/pause", bind(dtos.PauseAlertCommand{}), wrap(PauseAlert), reqEditorRole) - r.Post("/pause", bind(dtos.PauseAlertsCommand{}), wrap(PauseAlerts), reqGrafanaAdmin) r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert)) r.Get("/", wrap(GetAlerts)) r.Get("/states-for-dashboard", wrap(GetAlertStatesForDashboard)) @@ -290,6 +289,7 @@ func Register(r *macaron.Macaron) { r.Get("/users/:id/quotas", wrap(GetUserQuotas)) r.Put("/users/:id/quotas/:target", bind(m.UpdateUserQuotaCmd{}), wrap(UpdateUserQuota)) r.Get("/stats", AdminGetStats) + r.Post("/pause-all-alerts", bind(dtos.PauseAllAlertsCommand{}), wrap(PauseAlerts)) }, reqGrafanaAdmin) // rendering diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index c990b347aa3..f3d64f89524 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -65,7 +65,6 @@ type PauseAlertCommand struct { Paused bool `json:"paused"` } -type PauseAlertsCommand struct { - DataSourceIds []int64 `json:"datasourceId"` - Paused bool `json:"paused"` +type PauseAllAlertsCommand struct { + Paused bool `json:"paused"` } diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index f408aecba96..4dd2a7cedde 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -50,7 +50,7 @@ var ( // Timers M_DataSource_ProxyReq_Timer Timer - M_Alerting_Execution_Time Timer + M_Alerting_Execution_Time Timer // StatTotals M_Alerting_Active_Alerts Gauge diff --git a/pkg/models/alert.go b/pkg/models/alert.go index 3f7a5fb9c3f..13cae0715f7 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -3,6 +3,8 @@ package models import ( "time" + "fmt" + "github.com/grafana/grafana/pkg/components/simplejson" ) @@ -31,6 +33,10 @@ const ( ExecutionErrorKeepState ExecutionErrorOption = "keep_state" ) +var ( + ErrCannotChangeStateOnPausedAlert error = fmt.Errorf("Cannot change state on pause alert") +) + func (s AlertStateType) IsValid() bool { return s == AlertStateOK || s == AlertStateNoData || s == AlertStatePaused || s == AlertStatePending } @@ -138,6 +144,11 @@ type PauseAlertCommand struct { Paused bool } +type PauseAllAlertCommand struct { + ResultCount int64 + Paused bool +} + type SetAlertStateCommand struct { AlertId int64 OrgId int64 diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go index ca6c5f8b5d5..d7e0a0c29e3 100644 --- a/pkg/services/alerting/conditions/query.go +++ b/pkg/services/alerting/conditions/query.go @@ -34,10 +34,6 @@ type AlertQuery struct { To string } -func (c *QueryCondition) GetDatasourceId() (datasourceId *int64, exist bool) { - return &c.Query.DatasourceId, true -} - func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) { timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To) diff --git a/pkg/services/alerting/interfaces.go b/pkg/services/alerting/interfaces.go index 32c78b1dd62..566fbdb2898 100644 --- a/pkg/services/alerting/interfaces.go +++ b/pkg/services/alerting/interfaces.go @@ -30,5 +30,4 @@ type ConditionResult struct { type Condition interface { Eval(result *EvalContext) (*ConditionResult, error) - GetDatasourceId() (datasourceId *int64, exist bool) } diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index 2a7c6528b97..aab109088cb 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -86,6 +86,10 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { } if err := bus.Dispatch(cmd); err != nil { + if err == m.ErrCannotChangeStateOnPausedAlert { + handler.log.Error("Cannot change state on alert thats pause", "error", err) + return err + } handler.log.Error("Failed to save state", "error", err) } diff --git a/pkg/services/alerting/rule_test.go b/pkg/services/alerting/rule_test.go index 426712fa9f2..f8761efcd90 100644 --- a/pkg/services/alerting/rule_test.go +++ b/pkg/services/alerting/rule_test.go @@ -14,10 +14,6 @@ func (f *FakeCondition) Eval(context *EvalContext) (*ConditionResult, error) { return &ConditionResult{}, nil } -func (c *FakeCondition) GetDatasourceId() (datasourceId *int64, exist bool) { - return nil, false -} - func TestAlertRuleModel(t *testing.T) { Convey("Testing alert rule", t, func() { diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 819b5cf544d..1537a61dfa3 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -3,9 +3,8 @@ package sqlstore import ( "bytes" "fmt" - "time" - "strings" + "time" "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/bus" @@ -21,6 +20,7 @@ func init() { bus.AddHandler("sql", SetAlertState) bus.AddHandler("sql", GetAlertStatesForDashboard) bus.AddHandler("sql", PauseAlertRule) + bus.AddHandler("sql", PauseAllAlertRule) } func GetAlertById(query *m.GetAlertByIdQuery) error { @@ -230,6 +230,10 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error { return fmt.Errorf("Could not find alert") } + if alert.State == m.AlertStatePaused { + return m.ErrCannotChangeStateOnPausedAlert + } + alert.State = cmd.State alert.StateChanges += 1 alert.NewStateDate = time.Now() @@ -248,6 +252,10 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error { func PauseAlertRule(cmd *m.PauseAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { + if len(cmd.AlertIds) == 0 { + return fmt.Errorf("command contains no alertids") + } + var buffer bytes.Buffer params := make([]interface{}, 0) @@ -258,11 +266,9 @@ func PauseAlertRule(cmd *m.PauseAlertCommand) error { params = append(params, string(m.AlertStatePending)) } - if len(cmd.AlertIds) > 0 { - buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`) - for _, v := range cmd.AlertIds { - params = append(params, v) - } + buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`) + for _, v := range cmd.AlertIds { + params = append(params, v) } res, err := sess.Exec(buffer.String(), params...) @@ -274,6 +280,24 @@ func PauseAlertRule(cmd *m.PauseAlertCommand) error { }) } +func PauseAllAlertRule(cmd *m.PauseAllAlertCommand) error { + return inTransaction(func(sess *xorm.Session) error { + var newState string + if cmd.Paused { + newState = string(m.AlertStatePaused) + } else { + newState = string(m.AlertStatePending) + } + + res, err := sess.Exec(`UPDATE alert SET state = ?`, newState) + if err != nil { + return err + } + cmd.ResultCount, _ = res.RowsAffected() + return nil + }) +} + func GetAlertStatesForDashboard(query *m.GetAlertStatesForDashboardQuery) error { var rawSql = `SELECT id, diff --git a/pkg/services/sqlstore/alert_test.go b/pkg/services/sqlstore/alert_test.go index e22b1c48c47..0217def4772 100644 --- a/pkg/services/sqlstore/alert_test.go +++ b/pkg/services/sqlstore/alert_test.go @@ -39,6 +39,37 @@ func TestAlertingDataAccess(t *testing.T) { So(err, ShouldBeNil) }) + Convey("Can set new states", func() { + Convey("new state ok", func() { + cmd := &m.SetAlertStateCommand{ + AlertId: 1, + State: m.AlertStateOK, + } + + err = SetAlertState(cmd) + So(err, ShouldBeNil) + }) + + Convey("can pause alert", func() { + cmd := &m.PauseAllAlertCommand{ + Paused: true, + } + + err = PauseAllAlertRule(cmd) + So(err, ShouldBeNil) + + Convey("cannot updated paused alert", func() { + cmd := &m.SetAlertStateCommand{ + AlertId: 1, + State: m.AlertStateOK, + } + + err = SetAlertState(cmd) + So(err, ShouldNotBeNil) + }) + }) + }) + Convey("Can read properties", func() { alertQuery := m.GetAlertsQuery{DashboardId: testDash.Id, PanelId: 1, OrgId: 1} err2 := HandleAlertsQuery(&alertQuery) From a2257ec3d033d02f184780b05b7d4fbc9e7f38b4 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 19 Dec 2016 14:36:44 +0100 Subject: [PATCH 6/8] tech(alerting): renames pause all method --- pkg/api/alerting.go | 4 ++-- pkg/api/api.go | 2 +- pkg/services/alerting/eval_handler_test.go | 4 ---- pkg/services/sqlstore/alert.go | 8 ++++---- pkg/services/sqlstore/alert_test.go | 2 +- 5 files changed, 8 insertions(+), 12 deletions(-) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index f783e6e4229..245931eb7af 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -286,8 +286,8 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { return Json(200, result) } -//POST /api/alerts/pause -func PauseAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Response { +//POST /api/admin/pause-all-alerts +func PauseAllAlerts(c *middleware.Context, dto dtos.PauseAllAlertsCommand) Response { updateCmd := models.PauseAllAlertCommand{ Paused: dto.Paused, } diff --git a/pkg/api/api.go b/pkg/api/api.go index 4b142be1b1f..e931c5de601 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -289,7 +289,7 @@ func Register(r *macaron.Macaron) { r.Get("/users/:id/quotas", wrap(GetUserQuotas)) r.Put("/users/:id/quotas/:target", bind(m.UpdateUserQuotaCmd{}), wrap(UpdateUserQuota)) r.Get("/stats", AdminGetStats) - r.Post("/pause-all-alerts", bind(dtos.PauseAllAlertsCommand{}), wrap(PauseAlerts)) + r.Post("/pause-all-alerts", bind(dtos.PauseAllAlertsCommand{}), wrap(PauseAllAlerts)) }, reqGrafanaAdmin) // rendering diff --git a/pkg/services/alerting/eval_handler_test.go b/pkg/services/alerting/eval_handler_test.go index fd0d73f58b9..73d675e3fa7 100644 --- a/pkg/services/alerting/eval_handler_test.go +++ b/pkg/services/alerting/eval_handler_test.go @@ -18,10 +18,6 @@ func (c *conditionStub) Eval(context *EvalContext) (*ConditionResult, error) { return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator, NoDataFound: c.noData}, nil } -func (c *conditionStub) GetDatasourceId() (datasourceId *int64, exist bool) { - return nil, false -} - func TestAlertingExecutor(t *testing.T) { Convey("Test alert execution", t, func() { handler := NewEvalHandler() diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 1537a61dfa3..8718b03e786 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -19,8 +19,8 @@ func init() { bus.AddHandler("sql", GetAllAlertQueryHandler) bus.AddHandler("sql", SetAlertState) bus.AddHandler("sql", GetAlertStatesForDashboard) - bus.AddHandler("sql", PauseAlertRule) - bus.AddHandler("sql", PauseAllAlertRule) + bus.AddHandler("sql", PauseAlert) + bus.AddHandler("sql", PauseAllAlerts) } func GetAlertById(query *m.GetAlertByIdQuery) error { @@ -250,7 +250,7 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error { }) } -func PauseAlertRule(cmd *m.PauseAlertCommand) error { +func PauseAlert(cmd *m.PauseAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { if len(cmd.AlertIds) == 0 { return fmt.Errorf("command contains no alertids") @@ -280,7 +280,7 @@ func PauseAlertRule(cmd *m.PauseAlertCommand) error { }) } -func PauseAllAlertRule(cmd *m.PauseAllAlertCommand) error { +func PauseAllAlerts(cmd *m.PauseAllAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { var newState string if cmd.Paused { diff --git a/pkg/services/sqlstore/alert_test.go b/pkg/services/sqlstore/alert_test.go index 0217def4772..daf4f774717 100644 --- a/pkg/services/sqlstore/alert_test.go +++ b/pkg/services/sqlstore/alert_test.go @@ -55,7 +55,7 @@ func TestAlertingDataAccess(t *testing.T) { Paused: true, } - err = PauseAllAlertRule(cmd) + err = PauseAllAlerts(cmd) So(err, ShouldBeNil) Convey("cannot updated paused alert", func() { From b2c5a6a037c975390b8f0986d09b5013c5ce2b1b Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 19 Dec 2016 16:05:24 +0100 Subject: [PATCH 7/8] fix(alerting): pause dto can only pause one --- pkg/api/alerting.go | 9 ++++----- pkg/models/alert.go | 7 +++---- pkg/services/sqlstore/alert.go | 22 ++++------------------ 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 245931eb7af..0ec98018635 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -259,11 +259,10 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R //POST /api/alerts/:alertId/pause func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { - alertId := c.ParamsInt64("alertId") cmd := models.PauseAlertCommand{ - OrgId: c.OrgId, - AlertIds: []int64{alertId}, - Paused: dto.Paused, + OrgId: c.OrgId, + AlertId: c.ParamsInt64("alertId"), + Paused: dto.Paused, } if err := bus.Dispatch(&cmd); err != nil { @@ -278,7 +277,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { } result := map[string]interface{}{ - "alertId": alertId, + "alertId": c.ParamsInt64("alertId"), "state": response, "message": "alert " + pausedState, } diff --git a/pkg/models/alert.go b/pkg/models/alert.go index 13cae0715f7..176c146d51d 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -138,10 +138,9 @@ type SaveAlertsCommand struct { } type PauseAlertCommand struct { - OrgId int64 - AlertIds []int64 - ResultCount int64 - Paused bool + OrgId int64 + AlertId int64 + Paused bool } type PauseAllAlertCommand struct { diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 8718b03e786..7e3ea7de21b 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -3,7 +3,6 @@ package sqlstore import ( "bytes" "fmt" - "strings" "time" "github.com/go-xorm/xorm" @@ -252,31 +251,18 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error { func PauseAlert(cmd *m.PauseAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { - if len(cmd.AlertIds) == 0 { - return fmt.Errorf("command contains no alertids") - } - - var buffer bytes.Buffer params := make([]interface{}, 0) - buffer.WriteString(`UPDATE alert SET state = ?`) + sql := `UPDATE alert SET state = ? WHERE id = ?` if cmd.Paused { params = append(params, string(m.AlertStatePaused)) } else { params = append(params, string(m.AlertStatePending)) } + params = append(params, cmd.AlertId) - buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`) - for _, v := range cmd.AlertIds { - params = append(params, v) - } - - res, err := sess.Exec(buffer.String(), params...) - if err != nil { - return err - } - cmd.ResultCount, _ = res.RowsAffected() - return nil + _, err := sess.Exec(sql, params...) + return err }) } From 6c2c3c7e24205098be7c77cc73e2e5713aa82fff Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 19 Dec 2016 16:07:55 +0100 Subject: [PATCH 8/8] Revert "fix(alerting): pause dto can only pause one" This reverts commit b2c5a6a037c975390b8f0986d09b5013c5ce2b1b. --- pkg/api/alerting.go | 9 +++++---- pkg/models/alert.go | 7 ++++--- pkg/services/sqlstore/alert.go | 22 ++++++++++++++++++---- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 0ec98018635..245931eb7af 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -259,10 +259,11 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R //POST /api/alerts/:alertId/pause func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { + alertId := c.ParamsInt64("alertId") cmd := models.PauseAlertCommand{ - OrgId: c.OrgId, - AlertId: c.ParamsInt64("alertId"), - Paused: dto.Paused, + OrgId: c.OrgId, + AlertIds: []int64{alertId}, + Paused: dto.Paused, } if err := bus.Dispatch(&cmd); err != nil { @@ -277,7 +278,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { } result := map[string]interface{}{ - "alertId": c.ParamsInt64("alertId"), + "alertId": alertId, "state": response, "message": "alert " + pausedState, } diff --git a/pkg/models/alert.go b/pkg/models/alert.go index 176c146d51d..13cae0715f7 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -138,9 +138,10 @@ type SaveAlertsCommand struct { } type PauseAlertCommand struct { - OrgId int64 - AlertId int64 - Paused bool + OrgId int64 + AlertIds []int64 + ResultCount int64 + Paused bool } type PauseAllAlertCommand struct { diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 7e3ea7de21b..8718b03e786 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -3,6 +3,7 @@ package sqlstore import ( "bytes" "fmt" + "strings" "time" "github.com/go-xorm/xorm" @@ -251,18 +252,31 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error { func PauseAlert(cmd *m.PauseAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { + if len(cmd.AlertIds) == 0 { + return fmt.Errorf("command contains no alertids") + } + + var buffer bytes.Buffer params := make([]interface{}, 0) - sql := `UPDATE alert SET state = ? WHERE id = ?` + buffer.WriteString(`UPDATE alert SET state = ?`) if cmd.Paused { params = append(params, string(m.AlertStatePaused)) } else { params = append(params, string(m.AlertStatePending)) } - params = append(params, cmd.AlertId) - _, err := sess.Exec(sql, params...) - return err + buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`) + for _, v := range cmd.AlertIds { + params = append(params, v) + } + + res, err := sess.Exec(buffer.String(), params...) + if err != nil { + return err + } + cmd.ResultCount, _ = res.RowsAffected() + return nil }) }