diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 40b117835ec..e745f820aec 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -252,7 +252,7 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R return ApiSuccess("Test notification sent") } -//POST /api/:alertId/pause +//POST /api/alerts/:alertId/pause func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { cmd := models.PauseAlertCommand{ OrgId: c.OrgId, diff --git a/pkg/api/annotations.go b/pkg/api/annotations.go index 2803aa46435..48bf6c327ad 100644 --- a/pkg/api/annotations.go +++ b/pkg/api/annotations.go @@ -44,3 +44,19 @@ func GetAnnotations(c *middleware.Context) Response { return Json(200, result) } + +func DeleteAnnotations(c *middleware.Context, cmd dtos.DeleteAnnotationsCmd) Response { + repo := annotations.GetRepository() + + err := repo.Delete(&annotations.DeleteParams{ + AlertId: cmd.PanelId, + DashboardId: cmd.DashboardId, + PanelId: cmd.PanelId, + }) + + if err != nil { + return ApiError(500, "Failed to delete annotations", err) + } + + return ApiSuccess("Annotations deleted") +} diff --git a/pkg/api/api.go b/pkg/api/api.go index 5609536e213..211b5b05e4d 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -252,7 +252,7 @@ func Register(r *macaron.Macaron) { r.Group("/alerts", func() { r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest)) - r.Post("/:alertId/pause", ValidateOrgAlert, bind(dtos.PauseAlertCommand{}), wrap(PauseAlert)) + r.Post("/:alertId/pause", bind(dtos.PauseAlertCommand{}), wrap(PauseAlert)) r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert)) r.Get("/", wrap(GetAlerts)) r.Get("/states-for-dashboard", wrap(GetAlertStatesForDashboard)) @@ -269,6 +269,7 @@ func Register(r *macaron.Macaron) { }, reqOrgAdmin) r.Get("/annotations", wrap(GetAnnotations)) + r.Post("/annotations/mass-delete", reqOrgAdmin, bind(dtos.DeleteAnnotationsCmd{}), wrap(DeleteAnnotations)) // error test r.Get("/metrics/error", wrap(GenerateError)) diff --git a/pkg/api/dtos/annotations.go b/pkg/api/dtos/annotations.go index a5d5823e1a4..45415978ee1 100644 --- a/pkg/api/dtos/annotations.go +++ b/pkg/api/dtos/annotations.go @@ -15,3 +15,9 @@ type Annotation struct { Data *simplejson.Json `json:"data"` } + +type DeleteAnnotationsCmd struct { + AlertId int64 `json:"alertId"` + DashboardId int64 `json:"dashboardId"` + PanelId int64 `json:"panelId"` +} diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go index caf9d2cb56f..60d430b24b9 100644 --- a/pkg/cmd/grafana-server/main.go +++ b/pkg/cmd/grafana-server/main.go @@ -102,8 +102,10 @@ func writePIDFile() { func listenToSystemSignals(server models.GrafanaServer) { signalChan := make(chan os.Signal, 1) + ignoreChan := make(chan os.Signal, 1) code := 0 + signal.Notify(ignoreChan, syscall.SIGHUP) signal.Notify(signalChan, os.Interrupt, os.Kill, syscall.SIGTERM) select { diff --git a/pkg/services/annotations/annotations.go b/pkg/services/annotations/annotations.go index 189c3d823cf..3fc3bafe5c5 100644 --- a/pkg/services/annotations/annotations.go +++ b/pkg/services/annotations/annotations.go @@ -5,6 +5,7 @@ import "github.com/grafana/grafana/pkg/components/simplejson" type Repository interface { Save(item *Item) error Find(query *ItemQuery) ([]*Item, error) + Delete(params *DeleteParams) error } type ItemQuery struct { @@ -20,6 +21,12 @@ type ItemQuery struct { Limit int64 `json:"alertId"` } +type DeleteParams struct { + AlertId int64 `json:"alertId"` + DashboardId int64 `json:"dashboardId"` + PanelId int64 `json:"panelId"` +} + var repositoryInstance Repository func GetRepository() Repository { diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 76ea268bc04..4824b000bcb 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -46,13 +46,23 @@ func GetAllAlertQueryHandler(query *m.GetAllAlertsQuery) error { return nil } +func deleteAlertByIdInternal(alertId int64, reason string, sess *xorm.Session) error { + sqlog.Debug("Deleting alert", "id", alertId, "reason", reason) + + if _, err := sess.Exec("DELETE FROM alert WHERE id = ?", alertId); err != nil { + return err + } + + if _, err := sess.Exec("DELETE FROM annotation WHERE alert_id = ?", alertId); err != nil { + return err + } + + return nil +} + func DeleteAlertById(cmd *m.DeleteAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { - if _, err := sess.Exec("DELETE FROM alert WHERE id = ?", cmd.AlertId); err != nil { - return err - } - - return nil + return deleteAlertByIdInternal(cmd.AlertId, "DeleteAlertCommand", sess) }) } @@ -110,12 +120,7 @@ func DeleteAlertDefinition(dashboardId int64, sess *xorm.Session) error { sess.Where("dashboard_id = ?", dashboardId).Find(&alerts) for _, alert := range alerts { - _, err := sess.Exec("DELETE FROM alert WHERE id = ? ", alert.Id) - if err != nil { - return err - } - - sqlog.Debug("Alert deleted (due to dashboard deletion)", "name", alert.Name, "id", alert.Id) + deleteAlertByIdInternal(alert.Id, "Dashboard deleted", sess) } return nil @@ -195,12 +200,7 @@ func deleteMissingAlerts(alerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xorm } if missing { - _, err := sess.Exec("DELETE FROM alert WHERE id = ?", missingAlert.Id) - if err != nil { - return err - } - - sqlog.Debug("Alert deleted", "name", missingAlert.Name, "id", missingAlert.Id) + deleteAlertByIdInternal(missingAlert.Id, "Removed from dashboard", sess) } } @@ -248,7 +248,9 @@ func PauseAlertRule(cmd *m.PauseAlertCommand) error { return inTransaction(func(sess *xorm.Session) error { alert := m.Alert{} - if has, err := sess.Id(cmd.AlertId).Get(&alert); err != nil { + has, err := x.Where("id = ? AND org_id=?", cmd.AlertId, cmd.OrgId).Get(&alert) + + if err != nil { return err } else if !has { return fmt.Errorf("Could not find alert") diff --git a/pkg/services/sqlstore/annotation.go b/pkg/services/sqlstore/annotation.go index 3ea8647d3fa..e219f48d2fe 100644 --- a/pkg/services/sqlstore/annotation.go +++ b/pkg/services/sqlstore/annotation.go @@ -84,3 +84,17 @@ func (r *SqlAnnotationRepo) Find(query *annotations.ItemQuery) ([]*annotations.I return items, nil } + +func (r *SqlAnnotationRepo) Delete(params *annotations.DeleteParams) error { + return inTransaction(func(sess *xorm.Session) error { + + sql := "DELETE FROM annotation WHERE dashboard_id = ? AND panel_id = ?" + + _, err := sess.Exec(sql, params.DashboardId, params.PanelId) + if err != nil { + return err + } + + return nil + }) +} diff --git a/public/app/features/alerting/alert_tab_ctrl.ts b/public/app/features/alerting/alert_tab_ctrl.ts index b7e0e4ac218..61c4d658ed1 100644 --- a/public/app/features/alerting/alert_tab_ctrl.ts +++ b/public/app/features/alerting/alert_tab_ctrl.ts @@ -59,7 +59,7 @@ export class AlertTabCtrl { this.panelCtrl.render(); }); - // build notification model + // build notification model this.notifications = []; this.alertNotifications = []; this.alertHistory = []; @@ -352,6 +352,24 @@ export class AlertTabCtrl { this.evaluatorParamsChanged(); } + clearHistory() { + appEvents.emit('confirm-modal', { + title: 'Delete Alert History', + text: 'Are you sure you want to remove all history & annotations for this alert?', + icon: 'fa-trash', + yesText: 'Yes', + onConfirm: () => { + this.backendSrv.post('/api/annotations/mass-delete', { + dashboardId: this.panelCtrl.dashboard.id, + panelId: this.panel.id, + }).then(res => { + this.alertHistory = []; + this.panelCtrl.refresh(); + }); + } + }); + } + test() { this.testing = true; diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index 07ff28dcddb..4dd5516945b 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -125,7 +125,16 @@