Skip to content

Commit fb6aa0e

Browse files
committed
feat(api): adds endpoint for mass pausing alerts
ref grafana#6589
1 parent 830bf5a commit fb6aa0e

File tree

9 files changed

+116
-38
lines changed

9 files changed

+116
-38
lines changed

pkg/api/alerting.go

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,10 +259,11 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R
259259

260260
//POST /api/alerts/:alertId/pause
261261
func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
262+
alertId := c.ParamsInt64("alertId")
262263
cmd := models.PauseAlertCommand{
263-
OrgId: c.OrgId,
264-
AlertId: c.ParamsInt64("alertId"),
265-
Paused: dto.Paused,
264+
OrgId: c.OrgId,
265+
AlertIds: []int64{alertId},
266+
Paused: dto.Paused,
266267
}
267268

268269
if err := bus.Dispatch(&cmd); err != nil {
@@ -277,10 +278,73 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
277278
}
278279

279280
result := map[string]interface{}{
280-
"alertId": cmd.AlertId,
281+
"alertId": alertId,
281282
"state": response,
282283
"message": "alert " + pausedState,
283284
}
284285

285286
return Json(200, result)
286287
}
288+
289+
func existInSlice(slice []int64, value int64) bool {
290+
for _, v := range slice {
291+
if v == value {
292+
return true
293+
}
294+
}
295+
return false
296+
}
297+
298+
//POST /api/alerts/pause
299+
func PauseAlerts(c *middleware.Context, dto dtos.PauseAlertsCommand) Response {
300+
cmd := &models.GetAllAlertsQuery{}
301+
if err := bus.Dispatch(cmd); err != nil {
302+
return ApiError(500, "", err)
303+
}
304+
305+
var alertsToUpdate []int64
306+
skipFilter := len(dto.DataSourceIds) == 0
307+
for _, alert := range cmd.Result {
308+
if skipFilter {
309+
alertsToUpdate = append(alertsToUpdate, alert.Id)
310+
continue
311+
}
312+
313+
alert, err := alerting.NewRuleFromDBAlert(alert)
314+
if err != nil {
315+
return ApiError(500, "", err)
316+
}
317+
318+
for _, v := range alert.Conditions {
319+
id, exist := v.GetDatsourceId()
320+
if exist && existInSlice(dto.DataSourceIds, *id) {
321+
alertsToUpdate = append(alertsToUpdate, alert.Id)
322+
}
323+
}
324+
}
325+
326+
updateCmd := models.PauseAlertCommand{
327+
OrgId: c.OrgId,
328+
AlertIds: alertsToUpdate,
329+
Paused: dto.Paused,
330+
}
331+
332+
if err := bus.Dispatch(&updateCmd); err != nil {
333+
return ApiError(500, "", err)
334+
}
335+
336+
var response models.AlertStateType = models.AlertStatePending
337+
pausedState := "un paused"
338+
if updateCmd.Paused {
339+
response = models.AlertStatePaused
340+
pausedState = "paused"
341+
}
342+
343+
result := map[string]interface{}{
344+
"state": response,
345+
"message": "alert " + pausedState,
346+
"alertsAffected": updateCmd.ResultCount,
347+
}
348+
349+
return Json(200, result)
350+
}

pkg/api/alerting_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package api
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/smartystreets/goconvey/convey"
7+
)
8+
9+
func TestAlertingApi(t *testing.T) {
10+
Convey("", func() {})
11+
}

pkg/api/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ func Register(r *macaron.Macaron) {
256256
r.Group("/alerts", func() {
257257
r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest))
258258
r.Post("/:alertId/pause", bind(dtos.PauseAlertCommand{}), wrap(PauseAlert), reqEditorRole)
259+
r.Post("/pause", bind(dtos.PauseAlertsCommand{}), wrap(PauseAlerts), reqGrafanaAdmin)
259260
r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert))
260261
r.Get("/", wrap(GetAlerts))
261262
r.Get("/states-for-dashboard", wrap(GetAlertStatesForDashboard))

pkg/api/dtos/alerting.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,8 @@ type PauseAlertCommand struct {
6464
AlertId int64 `json:"alertId"`
6565
Paused bool `json:"paused"`
6666
}
67+
68+
type PauseAlertsCommand struct {
69+
DataSourceIds []int64 `json:"datasourceId"`
70+
Paused bool `json:"paused"`
71+
}

pkg/models/alert.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,10 @@ type SaveAlertsCommand struct {
132132
}
133133

134134
type PauseAlertCommand struct {
135-
OrgId int64
136-
AlertId int64
137-
Paused bool
135+
OrgId int64
136+
AlertIds []int64
137+
ResultCount int64
138+
Paused bool
138139
}
139140

140141
type SetAlertStateCommand struct {

pkg/services/alerting/conditions/query.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ type AlertQuery struct {
3434
To string
3535
}
3636

37+
func (c *QueryCondition) GetDatsourceId() (datasourceId *int64, exist bool) {
38+
return &c.Query.DatasourceId, true
39+
}
40+
3741
func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) {
3842
timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To)
3943

pkg/services/alerting/interfaces.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,5 @@ type ConditionResult struct {
3030

3131
type Condition interface {
3232
Eval(result *EvalContext) (*ConditionResult, error)
33+
GetDatsourceId() (datasourceId *int64, exist bool)
3334
}

pkg/services/sqlstore/alert.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"fmt"
66
"time"
77

8+
"strings"
9+
810
"github.com/go-xorm/xorm"
911
"github.com/grafana/grafana/pkg/bus"
1012
m "github.com/grafana/grafana/pkg/models"
@@ -246,25 +248,32 @@ func SetAlertState(cmd *m.SetAlertStateCommand) error {
246248

247249
func PauseAlertRule(cmd *m.PauseAlertCommand) error {
248250
return inTransaction(func(sess *xorm.Session) error {
249-
alert := m.Alert{}
250-
251-
has, err := x.Where("id = ? AND org_id=?", cmd.AlertId, cmd.OrgId).Get(&alert)
252-
253-
if err != nil {
254-
return err
255-
} else if !has {
256-
return fmt.Errorf("Could not find alert")
251+
var buffer bytes.Buffer
252+
params := make([]interface{}, 0)
253+
buffer.WriteString(`UPDATE alert SET state = ?`)
254+
255+
alertIdCount := len(cmd.AlertIds)
256+
if alertIdCount == 1 {
257+
buffer.WriteString(` WHERE id = ?`)
258+
} else if alertIdCount > 1 {
259+
buffer.WriteString(` WHERE id IN (?` + strings.Repeat(",?", len(cmd.AlertIds)-1) + `)`)
257260
}
258261

259-
var newState m.AlertStateType
260262
if cmd.Paused {
261-
newState = m.AlertStatePaused
263+
params = append(params, string(m.AlertStatePaused))
262264
} else {
263-
newState = m.AlertStatePending
265+
params = append(params, string(m.AlertStatePending))
264266
}
265-
alert.State = newState
266267

267-
sess.Id(alert.Id).Update(&alert)
268+
for _, v := range cmd.AlertIds {
269+
params = append(params, v)
270+
}
271+
272+
res, err := sess.Exec(buffer.String(), params...)
273+
if err != nil {
274+
return err
275+
}
276+
cmd.ResultCount, _ = res.RowsAffected()
268277
return nil
269278
})
270279
}

pkg/services/sqlstore/alert_heartbeat_test.go

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)