فهرست منبع

feat(alerting): add api endpoint for alert state

bergquist 9 سال پیش
والد
کامیت
e7be7d2835

+ 29 - 5
pkg/api/alerting.go

@@ -9,7 +9,7 @@ import (
 
 func ValidateOrgAlert(c *middleware.Context) {
 	id := c.ParamsInt64(":id")
-	query := models.GetAlertById{Id: id}
+	query := models.GetAlertByIdQuery{Id: id}
 
 	if err := bus.Dispatch(&query); err != nil {
 		c.JsonApiErr(404, "Alert not found", nil)
@@ -22,7 +22,7 @@ func ValidateOrgAlert(c *middleware.Context) {
 	}
 }
 
-// GET /api/alert_rule/changes
+// GET /api/alerts/changes
 func GetAlertChanges(c *middleware.Context) Response {
 	query := models.GetAlertChangesQuery{
 		OrgId: c.OrgId,
@@ -35,7 +35,7 @@ func GetAlertChanges(c *middleware.Context) Response {
 	return Json(200, query.Result)
 }
 
-// GET /api/alert_rule
+// GET /api/alerts
 func GetAlerts(c *middleware.Context) Response {
 	query := models.GetAlertsQuery{
 		OrgId: c.OrgId,
@@ -85,10 +85,10 @@ func GetAlerts(c *middleware.Context) Response {
 	return Json(200, alertDTOs)
 }
 
-// GET /api/alert_rule/:id
+// GET /api/alerts/:id
 func GetAlert(c *middleware.Context) Response {
 	id := c.ParamsInt64(":id")
-	query := models.GetAlertById{Id: id}
+	query := models.GetAlertByIdQuery{Id: id}
 
 	if err := bus.Dispatch(&query); err != nil {
 		return ApiError(500, "List alerts failed", err)
@@ -96,3 +96,27 @@ func GetAlert(c *middleware.Context) Response {
 
 	return Json(200, &query.Result)
 }
+
+// PUT /api/alerts/state/:id
+func PutAlertState(c *middleware.Context, cmd models.UpdateAlertStateCommand) Response {
+	alertId := c.ParamsInt64(":alertId")
+
+	if alertId != cmd.AlertId {
+		return ApiError(401, "Bad Request", nil)
+	}
+
+	query := models.GetAlertByIdQuery{Id: alertId}
+	if err := bus.Dispatch(&query); err != nil {
+		return ApiError(500, "Failed to get alertstate", err)
+	}
+
+	if query.Result.OrgId != 0 && query.Result.OrgId != c.OrgId {
+		return ApiError(500, "Alert not found", nil)
+	}
+
+	if err := bus.Dispatch(&cmd); err != nil {
+		return ApiError(500, "Failed to set new state", err)
+	}
+
+	return Json(200, cmd.Result)
+}

+ 2 - 1
pkg/api/api.go

@@ -238,7 +238,8 @@ func Register(r *macaron.Macaron) {
 		// metrics
 		r.Get("/metrics/test", GetTestMetrics)
 
-		r.Group("/alert_rule", func() {
+		r.Group("/alerts", func() {
+			r.Put("/state/:alertId", bind(m.UpdateAlertStateCommand{}), wrap(PutAlertState))
 			r.Get("/changes", wrap(GetAlertChanges))
 			r.Get("/", wrap(GetAlerts))
 			r.Get("/:id", ValidateOrgAlert, wrap(GetAlert))

+ 32 - 0
pkg/models/alerting_state.go

@@ -0,0 +1,32 @@
+package models
+
+import "time"
+
+type AlertStateLog struct {
+	Id           int64     `json:"id"`
+	OrgId        int64     `json:"-"`
+	AlertId      int64     `json:"alertId"`
+	State        string    `json:"type"`
+	Created      time.Time `json:"created"`
+	Acknowledged time.Time `json:"acknowledged"`
+	Deleted      time.Time `json:"deleted"`
+}
+
+var (
+	ALERT_STATE_OK           = "OK"
+	ALERT_STATE_ALERT        = "ALERT"
+	ALERT_STATE_WARN         = "WARN"
+	ALERT_STATE_ACKNOWLEDGED = "ACKNOWLEDGED"
+)
+
+func (this *UpdateAlertStateCommand) IsValidState() bool {
+	return this.NewState == ALERT_STATE_OK || this.NewState == ALERT_STATE_WARN || this.NewState == ALERT_STATE_ALERT || this.NewState == ALERT_STATE_ACKNOWLEDGED
+}
+
+type UpdateAlertStateCommand struct {
+	AlertId  int64  `json:"alertId" binding:"Required"`
+	NewState string `json:"newState" binding:"Required"`
+	Info     string `json:"info"`
+
+	Result *AlertRule
+}

+ 27 - 28
pkg/models/alerts.go

@@ -6,20 +6,20 @@ import (
 )
 
 type AlertRule struct {
-	Id             int64  `json:"id"`
-	OrgId          int64  `json:"-"`
-	DashboardId    int64  `json:"dashboardId"`
-	PanelId        int64  `json:"panelId"`
-	Query          string `json:"query"`
-	QueryRefId     string `json:"queryRefId"`
-	WarnLevel      string `json:"warnLevel"`
-	CritLevel      string `json:"critLevel"`
-	Interval       string `json:"interval"`
-	Title          string `json:"title"`
-	Description    string `json:"description"`
-	QueryRange     string `json:"queryRange"`
-	Aggregator     string `json:"aggregator"`
-	DatasourceName string `json:"-"`
+	Id          int64  `json:"id"`
+	OrgId       int64  `json:"-"`
+	DashboardId int64  `json:"dashboardId"`
+	PanelId     int64  `json:"panelId"`
+	Query       string `json:"query"`
+	QueryRefId  string `json:"queryRefId"`
+	WarnLevel   string `json:"warnLevel"`
+	CritLevel   string `json:"critLevel"`
+	Interval    string `json:"interval"`
+	Title       string `json:"title"`
+	Description string `json:"description"`
+	QueryRange  string `json:"queryRange"`
+	Aggregator  string `json:"aggregator"`
+	State       string `json:"state"`
 }
 
 type AlertRuleChange struct {
@@ -40,19 +40,18 @@ func (cmd *SaveDashboardCommand) GetAlertModels() *[]AlertRule {
 
 			alerting := panel.Get("alerting")
 			alert := AlertRule{
-				DashboardId:    cmd.Result.Id,
-				OrgId:          cmd.Result.OrgId,
-				PanelId:        panel.Get("id").MustInt64(),
-				DatasourceName: panel.Get("datasource").MustString(),
-				Id:             alerting.Get("id").MustInt64(),
-				QueryRefId:     alerting.Get("queryRef").MustString(),
-				WarnLevel:      alerting.Get("warnLevel").MustString(),
-				CritLevel:      alerting.Get("critLevel").MustString(),
-				Interval:       alerting.Get("interval").MustString(),
-				Title:          alerting.Get("title").MustString(),
-				Description:    alerting.Get("description").MustString(),
-				QueryRange:     alerting.Get("queryRange").MustString(),
-				Aggregator:     alerting.Get("aggregator").MustString(),
+				DashboardId: cmd.Result.Id,
+				OrgId:       cmd.Result.OrgId,
+				PanelId:     panel.Get("id").MustInt64(),
+				Id:          alerting.Get("id").MustInt64(),
+				QueryRefId:  alerting.Get("queryRef").MustString(),
+				WarnLevel:   alerting.Get("warnLevel").MustString(),
+				CritLevel:   alerting.Get("critLevel").MustString(),
+				Interval:    alerting.Get("interval").MustString(),
+				Title:       alerting.Get("title").MustString(),
+				Description: alerting.Get("description").MustString(),
+				QueryRange:  alerting.Get("queryRange").MustString(),
+				Aggregator:  alerting.Get("aggregator").MustString(),
 			}
 
 			for _, targetsObj := range panel.Get("targets").MustArray() {
@@ -92,7 +91,7 @@ type GetAlertsQuery struct {
 	Result []AlertRule
 }
 
-type GetAlertById struct {
+type GetAlertByIdQuery struct {
 	Id int64
 
 	Result AlertRule

+ 0 - 3
pkg/models/alerts_test.go

@@ -371,9 +371,6 @@ func TestAlertModel(t *testing.T) {
 
 			So(alerts[0].Query, ShouldEqual, `{"refId":"A","target":"aliasByNode(statsd.fakesite.counters.session_start.desktop.count, 4)"}`)
 			So(alerts[1].Query, ShouldEqual, `{"refId":"A","target":"aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}`)
-
-			So(alerts[0].DatasourceName, ShouldEqual, "")
-			So(alerts[1].DatasourceName, ShouldEqual, "graphite2")
 		})
 	})
 }

+ 2 - 3
pkg/services/sqlstore/alert_rule.go

@@ -13,7 +13,7 @@ func init() {
 	bus.AddHandler("sql", GetAlertById)
 }
 
-func GetAlertById(query *m.GetAlertById) error {
+func GetAlertById(query *m.GetAlertByIdQuery) error {
 	alert := m.AlertRule{}
 	has, err := x.Id(query.Id).Get(&alert)
 
@@ -68,7 +68,7 @@ func alertIsDifferent(rule1, rule2 m.AlertRule) bool {
 	result = result || rule1.Title != rule2.Title
 	result = result || rule1.Description != rule2.Description
 	result = result || rule1.QueryRange != rule2.QueryRange
-	result = result || rule1.DatasourceName != rule2.DatasourceName
+	//don't compare .State! That would be insane.
 
 	return result
 }
@@ -81,7 +81,6 @@ func SaveAlerts(cmd *m.SaveAlertsCommand) error {
 		}
 
 		upsertAlerts(alerts, cmd.Alerts, sess)
-
 		deleteMissingAlerts(alerts, cmd.Alerts, sess)
 
 		return nil

+ 12 - 14
pkg/services/sqlstore/alert_rule_test.go

@@ -16,19 +16,18 @@ func TestAlertingDataAccess(t *testing.T) {
 
 		items := []m.AlertRule{
 			{
-				PanelId:        1,
-				DashboardId:    testDash.Id,
-				OrgId:          testDash.OrgId,
-				Query:          "Query",
-				QueryRefId:     "A",
-				WarnLevel:      "> 30",
-				CritLevel:      "> 50",
-				Interval:       "10",
-				Title:          "Alerting title",
-				Description:    "Alerting description",
-				QueryRange:     "5m",
-				Aggregator:     "avg",
-				DatasourceName: "graphite",
+				PanelId:     1,
+				DashboardId: testDash.Id,
+				OrgId:       testDash.OrgId,
+				Query:       "Query",
+				QueryRefId:  "A",
+				WarnLevel:   "> 30",
+				CritLevel:   "> 50",
+				Interval:    "10",
+				Title:       "Alerting title",
+				Description: "Alerting description",
+				QueryRange:  "5m",
+				Aggregator:  "avg",
 			},
 		}
 
@@ -63,7 +62,6 @@ func TestAlertingDataAccess(t *testing.T) {
 			So(alert.Description, ShouldEqual, "Alerting description")
 			So(alert.QueryRange, ShouldEqual, "5m")
 			So(alert.Aggregator, ShouldEqual, "avg")
-			So(alert.DatasourceName, ShouldEqual, "graphite")
 		})
 
 		Convey("Alerts with same dashboard id and panel id should update", func() {

+ 39 - 0
pkg/services/sqlstore/alert_state.go

@@ -0,0 +1,39 @@
+package sqlstore
+
+import (
+	"fmt"
+	"github.com/go-xorm/xorm"
+	"github.com/grafana/grafana/pkg/bus"
+	m "github.com/grafana/grafana/pkg/models"
+)
+
+func init() {
+	bus.AddHandler("sql", SetNewAlertState)
+}
+
+func SetNewAlertState(cmd *m.UpdateAlertStateCommand) error {
+	return inTransaction(func(sess *xorm.Session) error {
+		if !cmd.IsValidState() {
+			return fmt.Errorf("new state is invalid")
+		}
+
+		alert := m.AlertRule{}
+		has, err := sess.Id(cmd.AlertId).Get(&alert)
+		if !has {
+			return fmt.Errorf("Could not find alert")
+		}
+
+		if err != nil {
+			return err
+		}
+
+		alert.State = cmd.NewState
+		sess.Id(alert.Id).Update(&alert)
+		//update alert
+
+		//insert alert state log
+
+		cmd.Result = &alert
+		return nil
+	})
+}

+ 85 - 0
pkg/services/sqlstore/alert_state_test.go

@@ -0,0 +1,85 @@
+package sqlstore
+
+import (
+	"testing"
+
+	m "github.com/grafana/grafana/pkg/models"
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+func TestAlertingStateAccess(t *testing.T) {
+	Convey("Test alerting state changes", t, func() {
+		InitTestDB(t)
+
+		//setup alert
+		testDash := insertTestDashboard("dashboard with alerts", 1, "alert")
+
+		items := []m.AlertRule{
+			{
+				PanelId:     1,
+				DashboardId: testDash.Id,
+				OrgId:       testDash.OrgId,
+				Query:       "Query",
+				QueryRefId:  "A",
+				WarnLevel:   "> 30",
+				CritLevel:   "> 50",
+				Interval:    "10",
+				Title:       "Alerting title",
+				Description: "Alerting description",
+				QueryRange:  "5m",
+				Aggregator:  "avg",
+			},
+		}
+
+		cmd := m.SaveAlertsCommand{
+			Alerts:      &items,
+			DashboardId: testDash.Id,
+			OrgId:       1,
+			UserId:      1,
+		}
+
+		err := SaveAlerts(&cmd)
+		So(err, ShouldBeNil)
+
+		Convey("Cannot insert invalid states", func() {
+			err = SetNewAlertState(&m.UpdateAlertStateCommand{
+				AlertId:  1,
+				NewState: "maybe ok",
+				Info:     "Shit just hit the fan",
+			})
+
+			So(err, ShouldNotBeNil)
+		})
+
+		Convey("Changes state to alert", func() {
+
+			err = SetNewAlertState(&m.UpdateAlertStateCommand{
+				AlertId:  1,
+				NewState: "ALERT",
+				Info:     "Shit just hit the fan",
+			})
+
+			Convey("can get new state for alert", func() {
+				query := &m.GetAlertByIdQuery{Id: 1}
+				err := GetAlertById(query)
+				So(err, ShouldBeNil)
+				So(query.Result.State, ShouldEqual, "ALERT")
+			})
+
+			Convey("Changes state to ok", func() {
+				err = SetNewAlertState(&m.UpdateAlertStateCommand{
+					AlertId:  1,
+					NewState: "OK",
+					Info:     "Shit just hit the fan",
+				})
+
+				Convey("get ok state for alert", func() {
+					query := &m.GetAlertByIdQuery{Id: 1}
+					err := GetAlertById(query)
+					So(err, ShouldBeNil)
+					So(query.Result.State, ShouldEqual, "OK")
+				})
+			})
+		})
+	})
+}

+ 1 - 1
pkg/services/sqlstore/migrations/alert_mig.go

@@ -19,7 +19,7 @@ func addAlertMigrations(mg *Migrator) {
 			{Name: "description", Type: DB_NVarchar, Length: 255, Nullable: false},
 			{Name: "query_range", Type: DB_NVarchar, Length: 255, Nullable: false},
 			{Name: "aggregator", Type: DB_NVarchar, Length: 255, Nullable: false},
-			{Name: "datasource_name", Type: DB_NVarchar, Length: 255, Nullable: false},
+			{Name: "state", Type: DB_NVarchar, Length: 255, Nullable: false},
 		},
 	}
 

+ 1 - 1
public/app/features/alerts/alerts_ctrl.ts

@@ -15,7 +15,7 @@ export class AlertPageCtrl {
   }
 
   loadAlerts() {
-    this.backendSrv.get('/api/alert_rule').then(result => {
+    this.backendSrv.get('/api/alerts').then(result => {
       console.log(result);
       this.alerts = result;
     });