diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 9525855573f..bb9c34a6a44 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -339,7 +339,7 @@ your Grafana instance. For example scopes = user:email,read:org auth_url = https://github.com/login/oauth/authorize token_url = https://github.com/login/oauth/access_token - allow_sign_up = false + allow_sign_up = true # space-delimited organization names allowed_organizations = github google diff --git a/pkg/api/cloudwatch/metrics.go b/pkg/api/cloudwatch/metrics.go index 5f87eccc87b..68843ba5774 100644 --- a/pkg/api/cloudwatch/metrics.go +++ b/pkg/api/cloudwatch/metrics.go @@ -140,7 +140,7 @@ func init() { func handleGetRegions(req *cwRequest, c *middleware.Context) { regions := []string{ "ap-northeast-1", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "cn-north-1", - "eu-central-1", "eu-west-1", "sa-east-1", "us-east-1", "us-west-1", "us-west-2", + "eu-central-1", "eu-west-1", "sa-east-1", "us-east-1", "us-west-1", "us-west-2", "us-gov-west-1", } result := []interface{}{} diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 481818aab23..25fa43d8d2f 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -9,42 +9,44 @@ func init() { } var ( - M_Instance_Start Counter - M_Page_Status_200 Counter - M_Page_Status_500 Counter - M_Page_Status_404 Counter - M_Page_Status_Unknown Counter - M_Api_Status_200 Counter - M_Api_Status_404 Counter - M_Api_Status_500 Counter - M_Api_Status_Unknown Counter - M_Proxy_Status_200 Counter - M_Proxy_Status_404 Counter - M_Proxy_Status_500 Counter - M_Proxy_Status_Unknown Counter - M_Api_User_SignUpStarted Counter - M_Api_User_SignUpCompleted Counter - M_Api_User_SignUpInvite Counter - M_Api_Dashboard_Save Timer - M_Api_Dashboard_Get Timer - M_Api_Dashboard_Search Timer - M_Api_Admin_User_Create Counter - M_Api_Login_Post Counter - M_Api_Login_OAuth Counter - M_Api_Org_Create Counter - M_Api_Dashboard_Snapshot_Create Counter - M_Api_Dashboard_Snapshot_External Counter - M_Api_Dashboard_Snapshot_Get Counter - M_Models_Dashboard_Insert Counter - M_Alerting_Result_State_Alerting Counter - M_Alerting_Result_State_Ok Counter - M_Alerting_Result_State_Paused Counter - M_Alerting_Result_State_NoData Counter - M_Alerting_Result_State_Pending Counter - M_Alerting_Active_Alerts Counter - M_Alerting_Notification_Sent_Slack Counter - M_Alerting_Notification_Sent_Email Counter - M_Alerting_Notification_Sent_Webhook Counter + M_Instance_Start Counter + M_Page_Status_200 Counter + M_Page_Status_500 Counter + M_Page_Status_404 Counter + M_Page_Status_Unknown Counter + M_Api_Status_200 Counter + M_Api_Status_404 Counter + M_Api_Status_500 Counter + M_Api_Status_Unknown Counter + M_Proxy_Status_200 Counter + M_Proxy_Status_404 Counter + M_Proxy_Status_500 Counter + M_Proxy_Status_Unknown Counter + M_Api_User_SignUpStarted Counter + M_Api_User_SignUpCompleted Counter + M_Api_User_SignUpInvite Counter + M_Api_Dashboard_Save Timer + M_Api_Dashboard_Get Timer + M_Api_Dashboard_Search Timer + M_Api_Admin_User_Create Counter + M_Api_Login_Post Counter + M_Api_Login_OAuth Counter + M_Api_Org_Create Counter + M_Api_Dashboard_Snapshot_Create Counter + M_Api_Dashboard_Snapshot_External Counter + M_Api_Dashboard_Snapshot_Get Counter + M_Models_Dashboard_Insert Counter + M_Alerting_Result_State_Alerting Counter + M_Alerting_Result_State_Ok Counter + M_Alerting_Result_State_Paused Counter + M_Alerting_Result_State_NoData Counter + M_Alerting_Result_State_Pending Counter + M_Alerting_Active_Alerts Counter + M_Alerting_Notification_Sent_Slack Counter + M_Alerting_Notification_Sent_Email Counter + M_Alerting_Notification_Sent_Webhook Counter + M_Alerting_Notification_Sent_PagerDuty Counter + // Timers M_DataSource_ProxyReq_Timer Timer @@ -107,6 +109,7 @@ func initMetricVars(settings *MetricSettings) { M_Alerting_Notification_Sent_Slack = RegCounter("alerting.notifications_sent", "type", "slack") M_Alerting_Notification_Sent_Email = RegCounter("alerting.notifications_sent", "type", "email") M_Alerting_Notification_Sent_Webhook = RegCounter("alerting.notifications_sent", "type", "webhook") + M_Alerting_Notification_Sent_PagerDuty = RegCounter("alerting.notifications_sent", "type", "pagerduty") // Timers M_DataSource_ProxyReq_Timer = RegTimer("api.dataproxy.request.all") diff --git a/pkg/models/notifications.go b/pkg/models/notifications.go index 80803b3be08..c43024da691 100644 --- a/pkg/models/notifications.go +++ b/pkg/models/notifications.go @@ -16,14 +16,6 @@ type SendEmailCommandSync struct { SendEmailCommand } -type SendWebhook struct { - Url string - User string - Password string - Body string - HttpMethod string -} - type SendWebhookSync struct { Url string User string diff --git a/pkg/services/alerting/conditions/reducer.go b/pkg/services/alerting/conditions/reducer.go index a982fa63d33..988a640591d 100644 --- a/pkg/services/alerting/conditions/reducer.go +++ b/pkg/services/alerting/conditions/reducer.go @@ -62,6 +62,15 @@ func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) null.Float { case "count": value = float64(len(series.Points)) allNull = false + case "last": + points := series.Points + for i := len(points) - 1; i >= 0; i-- { + if points[i][0].Valid { + value = points[i][0].Float64 + allNull = false + break + } + } } if allNull { diff --git a/pkg/services/alerting/conditions/reducer_test.go b/pkg/services/alerting/conditions/reducer_test.go index 198a52b746a..dfd229e1374 100644 --- a/pkg/services/alerting/conditions/reducer_test.go +++ b/pkg/services/alerting/conditions/reducer_test.go @@ -35,6 +35,12 @@ func TestSimpleReducer(t *testing.T) { result := testReducer("count", 1, 2, 3000) So(result, ShouldEqual, float64(3)) }) + + Convey("last", func() { + result := testReducer("last", 1, 2, 3000) + So(result, ShouldEqual, float64(3000)) + }) + }) } diff --git a/pkg/services/alerting/notifiers/pagerduty.go b/pkg/services/alerting/notifiers/pagerduty.go new file mode 100644 index 00000000000..1de34e07e5a --- /dev/null +++ b/pkg/services/alerting/notifiers/pagerduty.go @@ -0,0 +1,83 @@ +package notifiers + +import ( + "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" + "github.com/grafana/grafana/pkg/log" + "github.com/grafana/grafana/pkg/metrics" + m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/services/alerting" +) + +func init() { + alerting.RegisterNotifier("pagerduty", NewPagerdutyNotifier) +} + +var ( + pagerdutyEventApiUrl string = "https://events.pagerduty.com/generic/2010-04-15/create_event.json" +) + +func NewPagerdutyNotifier(model *m.AlertNotification) (alerting.Notifier, error) { + key := model.Settings.Get("integrationKey").MustString() + if key == "" { + return nil, alerting.ValidationError{Reason: "Could not find integration key property in settings"} + } + + return &PagerdutyNotifier{ + NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings), + Key: key, + log: log.New("alerting.notifier.pagerduty"), + }, nil +} + +type PagerdutyNotifier struct { + NotifierBase + Key string + log log.Logger +} + +func (this *PagerdutyNotifier) Notify(evalContext *alerting.EvalContext) error { + this.log.Info("Notifying Pagerduty") + metrics.M_Alerting_Notification_Sent_PagerDuty.Inc(1) + + if evalContext.Rule.State == m.AlertStateAlerting { + bodyJSON := simplejson.New() + bodyJSON.Set("service_key", this.Key) + bodyJSON.Set("description", evalContext.Rule.Name+" - "+evalContext.Rule.Message) + bodyJSON.Set("client", "Grafana") + bodyJSON.Set("event_type", "trigger") + + ruleUrl, err := evalContext.GetRuleUrl() + if err != nil { + this.log.Error("Failed get rule link", "error", err) + return err + } + bodyJSON.Set("client_url", ruleUrl) + + if evalContext.ImagePublicUrl != "" { + var contexts []interface{} + imageJSON := simplejson.New() + imageJSON.Set("type", "image") + imageJSON.Set("src", evalContext.ImagePublicUrl) + contexts[0] = imageJSON + bodyJSON.Set("contexts", contexts) + } + + body, _ := bodyJSON.MarshalJSON() + + cmd := &m.SendWebhookSync{ + Url: pagerdutyEventApiUrl, + Body: string(body), + HttpMethod: "POST", + } + + if err := bus.Dispatch(cmd); err != nil { + this.log.Error("Failed to send notification to Pagerduty", "error", err, "body", string(body)) + } + + } else { + this.log.Info("Not sending a trigger to Pagerduty", "state", evalContext.Rule.State) + } + + return nil +} diff --git a/pkg/services/alerting/notifiers/pagerduty_test.go b/pkg/services/alerting/notifiers/pagerduty_test.go new file mode 100644 index 00000000000..522bb133d77 --- /dev/null +++ b/pkg/services/alerting/notifiers/pagerduty_test.go @@ -0,0 +1,53 @@ +package notifiers + +import ( + "testing" + + "github.com/grafana/grafana/pkg/components/simplejson" + m "github.com/grafana/grafana/pkg/models" + . "github.com/smartystreets/goconvey/convey" +) + +func TestPagerdutyNotifier(t *testing.T) { + Convey("Pagerduty notifier tests", t, func() { + + Convey("Parsing alert notification from settings", func() { + Convey("empty settings should return error", func() { + json := `{ }` + + settingsJSON, _ := simplejson.NewJson([]byte(json)) + model := &m.AlertNotification{ + Name: "pageduty_testing", + Type: "pagerduty", + Settings: settingsJSON, + } + + _, err := NewPagerdutyNotifier(model) + So(err, ShouldNotBeNil) + }) + + Convey("settings should trigger incident", func() { + json := ` + { + "integrationKey": "abcdefgh0123456789" + }` + + settingsJSON, _ := simplejson.NewJson([]byte(json)) + model := &m.AlertNotification{ + Name: "pagerduty_testing", + Type: "pagerduty", + Settings: settingsJSON, + } + + not, err := NewPagerdutyNotifier(model) + pagerdutyNotifier := not.(*PagerdutyNotifier) + + So(err, ShouldBeNil) + So(pagerdutyNotifier.Name, ShouldEqual, "pagerduty_testing") + So(pagerdutyNotifier.Type, ShouldEqual, "pagerduty") + So(pagerdutyNotifier.Key, ShouldEqual, "abcdefgh0123456789") + }) + + }) + }) +} diff --git a/pkg/services/notifications/notifications.go b/pkg/services/notifications/notifications.go index 8a13eec2dc8..85bda1643d1 100644 --- a/pkg/services/notifications/notifications.go +++ b/pkg/services/notifications/notifications.go @@ -31,7 +31,6 @@ func Init() error { bus.AddCtxHandler("email", sendEmailCommandHandlerSync) - bus.AddHandler("webhook", sendWebhook) bus.AddCtxHandler("webhook", SendWebhookSync) bus.AddEventListener(signUpStartedHandler) @@ -69,18 +68,6 @@ func SendWebhookSync(ctx context.Context, cmd *m.SendWebhookSync) error { }) } -func sendWebhook(cmd *m.SendWebhook) error { - addToWebhookQueue(&Webhook{ - Url: cmd.Url, - User: cmd.User, - Password: cmd.Password, - Body: cmd.Body, - HttpMethod: cmd.HttpMethod, - }) - - return nil -} - func subjectTemplateFunc(obj map[string]interface{}, value string) string { obj["value"] = value return "" diff --git a/public/app/features/alerting/alert_def.ts b/public/app/features/alerting/alert_def.ts index aaddb9e425f..8ee1981f3d8 100644 --- a/public/app/features/alerting/alert_def.ts +++ b/public/app/features/alerting/alert_def.ts @@ -34,6 +34,7 @@ var reducerTypes = [ {text: 'max()', value: 'max'}, {text: 'sum()' , value: 'sum'}, {text: 'count()', value: 'count'}, + {text: 'last()', value: 'last'}, ]; var noDataModes = [ diff --git a/public/app/features/alerting/alert_list_ctrl.ts b/public/app/features/alerting/alert_list_ctrl.ts index b2287759a9b..5e3cce35c6e 100644 --- a/public/app/features/alerting/alert_list_ctrl.ts +++ b/public/app/features/alerting/alert_list_ctrl.ts @@ -15,7 +15,6 @@ export class AlertListCtrl { {text: 'OK', value: 'ok'}, {text: 'Alerting', value: 'alerting'}, {text: 'No Data', value: 'no_data'}, - {text: 'Execution Error', value: 'execution_error'}, ]; filters = { diff --git a/public/app/features/alerting/alert_tab_ctrl.ts b/public/app/features/alerting/alert_tab_ctrl.ts index e505f9be361..c882a6ac3bf 100644 --- a/public/app/features/alerting/alert_tab_ctrl.ts +++ b/public/app/features/alerting/alert_tab_ctrl.ts @@ -90,6 +90,7 @@ export class AlertTabCtrl { case "email": return "fa fa-envelope"; case "slack": return "fa fa-slack"; case "webhook": return "fa fa-cubes"; + case "pagerduty": return "fa fa-bullhorn"; } } diff --git a/public/app/features/alerting/partials/notification_edit.html b/public/app/features/alerting/partials/notification_edit.html index efb22e2e05a..7ee96e60c8a 100644 --- a/public/app/features/alerting/partials/notification_edit.html +++ b/public/app/features/alerting/partials/notification_edit.html @@ -19,7 +19,7 @@