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 - - }) -}