ソースを参照

feat(alerting): add support for alert_rule updates

bergquist 9 年 前
コミット
25f6ec8b53

+ 10 - 0
pkg/models/alerts.go

@@ -2,10 +2,12 @@ package models
 
 import (
 	"github.com/grafana/grafana/pkg/components/simplejson"
+	"time"
 )
 
 type AlertRule struct {
 	Id          int64
+	OrgId       int64
 	DashboardId int64
 	PanelId     int64
 	Query       string
@@ -19,6 +21,13 @@ type AlertRule struct {
 	Aggregator  string
 }
 
+type AlertRuleChange struct {
+	OrgId   int64
+	AlertId int64
+	Type    string
+	Created time.Time
+}
+
 func (cmd *SaveDashboardCommand) GetAlertModels() *[]AlertRule {
 	alerts := make([]AlertRule, 0)
 
@@ -31,6 +40,7 @@ func (cmd *SaveDashboardCommand) GetAlertModels() *[]AlertRule {
 			alerting := panel.Get("alerting")
 			alert := AlertRule{
 				DashboardId: cmd.Result.Id,
+				OrgId:       cmd.Result.OrgId,
 				PanelId:     panel.Get("id").MustInt64(),
 				Id:          alerting.Get("id").MustInt64(),
 				QueryRefId:  alerting.Get("query_ref").MustString(),

+ 59 - 3
pkg/services/sqlstore/alerting.go

@@ -2,14 +2,49 @@ package sqlstore
 
 import (
 	"fmt"
+	"github.com/go-xorm/xorm"
 	"github.com/grafana/grafana/pkg/bus"
 	m "github.com/grafana/grafana/pkg/models"
+	"time"
 )
 
 func init() {
 	bus.AddHandler("sql", SaveAlerts)
 }
 
+func SaveAlertChange(change string, alert m.AlertRule) error {
+	return inTransaction(func(sess *xorm.Session) error {
+		_, err := sess.Insert(&m.AlertRuleChange{
+			OrgId:   alert.OrgId,
+			Type:    change,
+			Created: time.Now(),
+			AlertId: alert.Id,
+		})
+
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+}
+
+func alertIsDifferent(rule1, rule2 m.AlertRule) bool {
+	result := false
+
+	result = result || rule1.Aggregator != rule2.Aggregator
+	result = result || rule1.CritLevel != rule2.CritLevel
+	result = result || rule1.WarnLevel != rule2.WarnLevel
+	result = result || rule1.Query != rule2.Query
+	result = result || rule1.QueryRefId != rule2.QueryRefId
+	result = result || rule1.Interval != rule2.Interval
+	result = result || rule1.Title != rule2.Title
+	result = result || rule1.Description != rule2.Description
+	result = result || rule1.QueryRange != rule2.QueryRange
+
+	return result
+}
+
 func SaveAlerts(cmd *m.SaveAlertsCommand) error {
 	//this function should be refactored
 
@@ -22,24 +57,33 @@ func SaveAlerts(cmd *m.SaveAlertsCommand) error {
 
 	for _, alert := range *cmd.Alerts {
 		update := false
+		var alertToUpdate m.AlertRule
 
 		for _, k := range alerts {
 			if alert.PanelId == k.PanelId {
 				update = true
 				alert.Id = k.Id
+				alertToUpdate = k
 			}
 		}
 
 		if update {
-			_, err = x.Id(alert.Id).Update(&alert)
-			if err != nil {
-				return err
+
+			if alertIsDifferent(alertToUpdate, alert) {
+				_, err = x.Id(alert.Id).Update(&alert)
+				if err != nil {
+					return err
+				}
+
+				SaveAlertChange("UPDATED", alert)
 			}
+
 		} else {
 			_, err = x.Insert(&alert)
 			if err != nil {
 				return err
 			}
+			SaveAlertChange("CREATED", alert)
 		}
 	}
 
@@ -58,6 +102,7 @@ func SaveAlerts(cmd *m.SaveAlertsCommand) error {
 				return err
 			}
 
+			err = SaveAlertChange("DELETED", missingAlert)
 			if err != nil {
 				return err
 			}
@@ -95,3 +140,14 @@ func GetAlertsByDashboardAndPanelId(dashboardId, panelId int64) (m.AlertRule, er
 
 	return alerts[0], nil
 }
+
+func GetAlertRuleChanges(orgid int64) ([]m.AlertRuleChange, error) {
+	alertChanges := make([]m.AlertRuleChange, 0)
+	err := x.Where("org_id = ?", orgid).Find(&alertChanges)
+
+	if err != nil {
+		return []m.AlertRuleChange{}, err
+	}
+
+	return alertChanges, nil
+}

+ 26 - 0
pkg/services/sqlstore/alerting_test.go

@@ -18,6 +18,7 @@ func TestAlertingDataAccess(t *testing.T) {
 			{
 				PanelId:     1,
 				DashboardId: testDash.Id,
+				OrgId:       testDash.OrgId,
 				Query:       "Query",
 				QueryRefId:  "A",
 				WarnLevel:   "> 30",
@@ -41,6 +42,10 @@ func TestAlertingDataAccess(t *testing.T) {
 
 		Convey("Can create one alert", func() {
 			So(err, ShouldBeNil)
+
+			alertChanges, er := GetAlertRuleChanges(1)
+			So(er, ShouldBeNil)
+			So(len(alertChanges), ShouldEqual, 1)
 		})
 
 		Convey("Can read properties", func() {
@@ -82,6 +87,15 @@ func TestAlertingDataAccess(t *testing.T) {
 				So(len(alerts), ShouldEqual, 1)
 				So(alerts[0].Query, ShouldEqual, "Updated Query")
 			})
+
+			Convey("Updates without changes should be ignored", func() {
+				err3 := SaveAlerts(&modifiedCmd)
+				So(err3, ShouldBeNil)
+
+				alertChanges, er := GetAlertRuleChanges(1)
+				So(er, ShouldBeNil)
+				So(len(alertChanges), ShouldEqual, 2)
+			})
 		})
 
 		Convey("Multiple alerts per dashboard", func() {
@@ -90,16 +104,19 @@ func TestAlertingDataAccess(t *testing.T) {
 					DashboardId: testDash.Id,
 					PanelId:     1,
 					Query:       "1",
+					OrgId:       1,
 				},
 				{
 					DashboardId: testDash.Id,
 					PanelId:     2,
 					Query:       "2",
+					OrgId:       1,
 				},
 				{
 					DashboardId: testDash.Id,
 					PanelId:     3,
 					Query:       "3",
+					OrgId:       1,
 				},
 			}
 
@@ -112,6 +129,9 @@ func TestAlertingDataAccess(t *testing.T) {
 				alerts, err2 := GetAlertsByDashboardId(testDash.Id)
 				So(err2, ShouldBeNil)
 				So(len(alerts), ShouldEqual, 3)
+				alertChanges, er := GetAlertRuleChanges(1)
+				So(er, ShouldBeNil)
+				So(len(alertChanges), ShouldEqual, 4)
 			})
 
 			Convey("should updated two dashboards and delete one", func() {
@@ -125,6 +145,12 @@ func TestAlertingDataAccess(t *testing.T) {
 					So(err2, ShouldBeNil)
 					So(len(alerts), ShouldEqual, 2)
 				})
+
+				Convey("should add one more alert_rule_change", func() {
+					alertChanges, er := GetAlertRuleChanges(1)
+					So(er, ShouldBeNil)
+					So(len(alertChanges), ShouldEqual, 6)
+				})
 			})
 		})
 

+ 4 - 2
pkg/services/sqlstore/migrations/alert_mig.go

@@ -9,6 +9,7 @@ func addAlertMigrations(mg *Migrator) {
 			{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
 			{Name: "dashboard_id", Type: DB_BigInt, Nullable: false},
 			{Name: "panel_id", Type: DB_BigInt, Nullable: false},
+			{Name: "org_id", Type: DB_BigInt, Nullable: false},
 			{Name: "query", Type: DB_Text, Nullable: false},
 			{Name: "query_ref_id", Type: DB_NVarchar, Length: 255, Nullable: false},
 			{Name: "warn_level", Type: DB_NVarchar, Length: 255, Nullable: false},
@@ -22,14 +23,15 @@ func addAlertMigrations(mg *Migrator) {
 	}
 
 	// create table
-	mg.AddMigration("create alert table v1", NewAddTableMigration(alertV1))
+	mg.AddMigration("create alert_rule table v1", NewAddTableMigration(alertV1))
 
 	alert_changes := Table{
-		Name: "alert_rule_updates",
+		Name: "alert_rule_change",
 		Columns: []*Column{
 			{Name: "id", Type: DB_BigInt, IsPrimaryKey: true, IsAutoIncrement: true},
 			{Name: "alert_id", Type: DB_BigInt, Nullable: false},
 			{Name: "org_id", Type: DB_BigInt, Nullable: false},
+			{Name: "type", Type: DB_NVarchar, Length: 50, Nullable: false},
 			{Name: "created", Type: DB_DateTime, Nullable: false},
 		},
 	}