From 56ccd80cce336d0f41329fa3dc58aa599a3c927b Mon Sep 17 00:00:00 2001 From: Noah Heil Date: Mon, 24 Oct 2016 03:05:16 -0600 Subject: [PATCH 01/34] Added two hints to help newer users I spent a couple of hours trying to figure out why the cloudwatch datasource was not working and the "internal error" notice was not helpful. So I added some hints so that people who are a little newer to linux/aws/grafana wont have to suffer like I did. --- docs/sources/datasources/cloudwatch.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sources/datasources/cloudwatch.md b/docs/sources/datasources/cloudwatch.md index 2c2acb2ca56..ae909734617 100644 --- a/docs/sources/datasources/cloudwatch.md +++ b/docs/sources/datasources/cloudwatch.md @@ -25,6 +25,7 @@ be ready to build dashboards for you CloudWatch metrics. 3. Click the `Add new` link in the top header. 4. Select `CloudWatch` from the dropdown. + > NOTE: If at any moment you have issues with getting this datasource to work and grafana is giving you undescriptive errors then dont forget to check your log file (try looking in /var/log/grafana/). Name | Description ------------ | ------------- @@ -47,6 +48,7 @@ Checkout AWS docs on [IAM Roles](http://docs.aws.amazon.com/AWSEC2/latest/UserGu ### AWS credentials file Create a file at `~/.aws/credentials`. That is the `HOME` path for user running grafana-server. + > NOTE: If you think you have the credentials file in the right place but it is still not working then you might try moving your .aws file to '/usr/share/grafana/' and make sure your credentials file has at most 0644 permissions. Example content: From 4c64e45a59c7d3f1f0f4e0f7c57323a84e7c6d15 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Fri, 28 Oct 2016 14:44:45 -0700 Subject: [PATCH 02/34] Added initalized state to alerts --- pkg/metrics/metrics.go | 2 ++ pkg/models/alert.go | 13 +++++++------ pkg/services/alerting/result_handler.go | 9 ++++++++- pkg/services/sqlstore/alert.go | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 002f2369c9b..f2616b3444c 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -41,6 +41,7 @@ var ( M_Alerting_Result_State_Paused Counter M_Alerting_Result_State_NoData Counter M_Alerting_Result_State_ExecError Counter + M_Alerting_Result_State_Initialized Counter M_Alerting_Active_Alerts Counter M_Alerting_Notification_Sent_Slack Counter M_Alerting_Notification_Sent_Email Counter @@ -102,6 +103,7 @@ func initMetricVars(settings *MetricSettings) { M_Alerting_Result_State_Paused = RegCounter("alerting.result", "state", "paused") M_Alerting_Result_State_NoData = RegCounter("alerting.result", "state", "no_data") M_Alerting_Result_State_ExecError = RegCounter("alerting.result", "state", "exec_error") + M_Alerting_Result_State_Initialized = RegCounter("alerting.result", "state", "initialized") M_Alerting_Active_Alerts = RegCounter("alerting.active_alerts") M_Alerting_Notification_Sent_Slack = RegCounter("alerting.notifications_sent", "type", "slack") diff --git a/pkg/models/alert.go b/pkg/models/alert.go index 7531be90e88..b0d97756955 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -11,11 +11,12 @@ type AlertSeverityType string type NoDataOption string const ( - AlertStateNoData AlertStateType = "no_data" - AlertStateExecError AlertStateType = "execution_error" - AlertStatePaused AlertStateType = "paused" - AlertStateAlerting AlertStateType = "alerting" - AlertStateOK AlertStateType = "ok" + AlertStateNoData AlertStateType = "no_data" + AlertStateExecError AlertStateType = "execution_error" + AlertStatePaused AlertStateType = "paused" + AlertStateAlerting AlertStateType = "alerting" + AlertStateOK AlertStateType = "ok" + AlertStateInitialized AlertStateType = "initialized" ) const ( @@ -26,7 +27,7 @@ const ( ) func (s AlertStateType) IsValid() bool { - return s == AlertStateOK || s == AlertStateNoData || s == AlertStateExecError || s == AlertStatePaused + return s == AlertStateOK || s == AlertStateNoData || s == AlertStateExecError || s == AlertStatePaused || s == AlertStateInitialized } func (s NoDataOption) IsValid() bool { diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index d786e8d599d..323c8776f4a 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -86,7 +86,12 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { handler.log.Error("Failed to save annotation for new alert state", "error", err) } - handler.notifier.Notify(evalContext) + if (oldState == m.AlertStateInitialized) && (evalContext.Rule.State == m.AlertStateOK) { + handler.log.Info("Notfication not sent", "oldState", oldState, "newState", evalContext.Rule.State) + } else { + handler.notifier.Notify(evalContext) + } + } return nil @@ -98,6 +103,8 @@ func (handler *DefaultResultHandler) shouldUpdateAlertState(evalContext *EvalCon func countStateResult(state m.AlertStateType) { switch state { + case m.AlertStateInitialized: + metrics.M_Alerting_Result_State_Initialized.Inc(1) case m.AlertStateAlerting: metrics.M_Alerting_Result_State_Alerting.Inc(1) case m.AlertStateOK: diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 4824b000bcb..4eca93a10d6 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -173,7 +173,7 @@ func upsertAlerts(existingAlerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xor } else { alert.Updated = time.Now() alert.Created = time.Now() - alert.State = m.AlertStateNoData + alert.State = m.AlertStateInitialized alert.NewStateDate = time.Now() _, err := sess.Insert(alert) From dcaae47e96da9abc40993a5cf837f94724a7f7d1 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Fri, 28 Oct 2016 14:54:55 -0700 Subject: [PATCH 03/34] Fixed tests --- pkg/services/sqlstore/alert_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/services/sqlstore/alert_test.go b/pkg/services/sqlstore/alert_test.go index 02126c2984a..3aba605bb5c 100644 --- a/pkg/services/sqlstore/alert_test.go +++ b/pkg/services/sqlstore/alert_test.go @@ -47,7 +47,7 @@ func TestAlertingDataAccess(t *testing.T) { So(err2, ShouldBeNil) So(alert.Name, ShouldEqual, "Alerting title") So(alert.Message, ShouldEqual, "Alerting message") - So(alert.State, ShouldEqual, "no_data") + So(alert.State, ShouldEqual, "initialized") So(alert.Frequency, ShouldEqual, 1) }) @@ -77,7 +77,7 @@ func TestAlertingDataAccess(t *testing.T) { So(query.Result[0].Name, ShouldEqual, "Name") Convey("Alert state should not be updated", func() { - So(query.Result[0].State, ShouldEqual, "no_data") + So(query.Result[0].State, ShouldEqual, "initialized") }) }) From 80fdd830de4318c69dce0a7fc9e866cb91b9462b Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Mon, 31 Oct 2016 14:46:51 -0700 Subject: [PATCH 04/34] Added override default recipient functionality --- pkg/services/alerting/notifiers/slack.go | 9 +++++++++ .../features/alerting/partials/notification_edit.html | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/pkg/services/alerting/notifiers/slack.go b/pkg/services/alerting/notifiers/slack.go index 7238af179d7..2248c0bac63 100644 --- a/pkg/services/alerting/notifiers/slack.go +++ b/pkg/services/alerting/notifiers/slack.go @@ -22,9 +22,12 @@ func NewSlackNotifier(model *m.AlertNotification) (alerting.Notifier, error) { return nil, alerting.ValidationError{Reason: "Could not find url property in settings"} } + recipient := model.Settings.Get("recipient").MustString() + return &SlackNotifier{ NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings), Url: url, + Recipient: recipient, log: log.New("alerting.notifier.slack"), }, nil } @@ -32,6 +35,7 @@ func NewSlackNotifier(model *m.AlertNotification) (alerting.Notifier, error) { type SlackNotifier struct { NotifierBase Url string + Recipient string log log.Logger } @@ -87,6 +91,11 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error { }, } + //recipient override + if this.Recipient != "" { + body["channel"] = this.Recipient + } + data, _ := json.Marshal(&body) cmd := &m.SendWebhookSync{Url: this.Url, Body: string(data)} diff --git a/public/app/features/alerting/partials/notification_edit.html b/public/app/features/alerting/partials/notification_edit.html index c0bbf49f77b..6e7532a3d24 100644 --- a/public/app/features/alerting/partials/notification_edit.html +++ b/public/app/features/alerting/partials/notification_edit.html @@ -63,6 +63,13 @@ Url +
+ Recipient + + +
From 95117a0bc94568d29d10451a1fddbd19edfdfe0e Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Mon, 31 Oct 2016 14:55:21 -0700 Subject: [PATCH 05/34] Added message parsing functionality --- pkg/services/alerting/notifiers/slack.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/services/alerting/notifiers/slack.go b/pkg/services/alerting/notifiers/slack.go index 2248c0bac63..0e29af39e79 100644 --- a/pkg/services/alerting/notifiers/slack.go +++ b/pkg/services/alerting/notifiers/slack.go @@ -89,6 +89,7 @@ func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error { "ts": time.Now().Unix(), }, }, + "parse": "full", // to linkify urls, users and channels in alert message. } //recipient override From c6257b30c1a7140c18a865faf7c6ee1b04a33114 Mon Sep 17 00:00:00 2001 From: David Moravek Date: Mon, 31 Oct 2016 23:14:48 +0100 Subject: [PATCH 06/34] Take grafana-cli proxy settings from env --- pkg/cmd/grafana-cli/services/services.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/cmd/grafana-cli/services/services.go b/pkg/cmd/grafana-cli/services/services.go index 08a4bf4693e..8f901046edb 100644 --- a/pkg/cmd/grafana-cli/services/services.go +++ b/pkg/cmd/grafana-cli/services/services.go @@ -25,6 +25,7 @@ func Init(version string) { grafanaVersion = version tr := &http.Transport{ + Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, } From 63e2337f6cf88430498fa7fcd4dfc0b04d817ed6 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 1 Nov 2016 08:46:31 +0100 Subject: [PATCH 07/34] feat(alerting): change placeholder to info-popover --- .../alerting/partials/notification_edit.html | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/public/app/features/alerting/partials/notification_edit.html b/public/app/features/alerting/partials/notification_edit.html index 6e7532a3d24..ede9cc247dd 100644 --- a/public/app/features/alerting/partials/notification_edit.html +++ b/public/app/features/alerting/partials/notification_edit.html @@ -59,16 +59,20 @@

Slack settings

-
+
Url
-
+
Recipient - + data-placement="right"> + + Override default channel or user, use #channel-name or <@username></@username> +
From 578507ae77798bdc5f20b7982624e84be68f7880 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 1 Nov 2016 08:58:44 +0100 Subject: [PATCH 08/34] tech(cli): add default settings for transport --- pkg/cmd/grafana-cli/services/services.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/grafana-cli/services/services.go b/pkg/cmd/grafana-cli/services/services.go index 8f901046edb..6c739f8b0ee 100644 --- a/pkg/cmd/grafana-cli/services/services.go +++ b/pkg/cmd/grafana-cli/services/services.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io/ioutil" + "net" "net/http" "net/url" "path" @@ -25,7 +26,16 @@ func Init(version string) { grafanaVersion = version tr := &http.Transport{ - Proxy: http.ProxyFromEnvironment, + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + TLSClientConfig: &tls.Config{InsecureSkipVerify: false}, } From d9f2519916775ddad66609fbaa4569409e749c49 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 1 Nov 2016 07:30:55 -0700 Subject: [PATCH 09/34] Renamed Initialized to Pending --- pkg/metrics/metrics.go | 4 ++-- pkg/models/alert.go | 4 ++-- pkg/services/alerting/result_handler.go | 6 +++--- pkg/services/sqlstore/alert.go | 2 +- pkg/services/sqlstore/alert_test.go | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index f2616b3444c..1cdcc71e132 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -41,7 +41,7 @@ var ( M_Alerting_Result_State_Paused Counter M_Alerting_Result_State_NoData Counter M_Alerting_Result_State_ExecError Counter - M_Alerting_Result_State_Initialized Counter + M_Alerting_Result_State_Pending Counter M_Alerting_Active_Alerts Counter M_Alerting_Notification_Sent_Slack Counter M_Alerting_Notification_Sent_Email Counter @@ -103,7 +103,7 @@ func initMetricVars(settings *MetricSettings) { M_Alerting_Result_State_Paused = RegCounter("alerting.result", "state", "paused") M_Alerting_Result_State_NoData = RegCounter("alerting.result", "state", "no_data") M_Alerting_Result_State_ExecError = RegCounter("alerting.result", "state", "exec_error") - M_Alerting_Result_State_Initialized = RegCounter("alerting.result", "state", "initialized") + M_Alerting_Result_State_Pending = RegCounter("alerting.result", "state", "pending") M_Alerting_Active_Alerts = RegCounter("alerting.active_alerts") M_Alerting_Notification_Sent_Slack = RegCounter("alerting.notifications_sent", "type", "slack") diff --git a/pkg/models/alert.go b/pkg/models/alert.go index b0d97756955..ec2e8e18841 100644 --- a/pkg/models/alert.go +++ b/pkg/models/alert.go @@ -16,7 +16,7 @@ const ( AlertStatePaused AlertStateType = "paused" AlertStateAlerting AlertStateType = "alerting" AlertStateOK AlertStateType = "ok" - AlertStateInitialized AlertStateType = "initialized" + AlertStatePending AlertStateType = "pending" ) const ( @@ -27,7 +27,7 @@ const ( ) func (s AlertStateType) IsValid() bool { - return s == AlertStateOK || s == AlertStateNoData || s == AlertStateExecError || s == AlertStatePaused || s == AlertStateInitialized + return s == AlertStateOK || s == AlertStateNoData || s == AlertStateExecError || s == AlertStatePaused || s == AlertStatePending } func (s NoDataOption) IsValid() bool { diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index 323c8776f4a..d2ad9345416 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -86,7 +86,7 @@ func (handler *DefaultResultHandler) Handle(evalContext *EvalContext) error { handler.log.Error("Failed to save annotation for new alert state", "error", err) } - if (oldState == m.AlertStateInitialized) && (evalContext.Rule.State == m.AlertStateOK) { + if (oldState == m.AlertStatePending) && (evalContext.Rule.State == m.AlertStateOK) { handler.log.Info("Notfication not sent", "oldState", oldState, "newState", evalContext.Rule.State) } else { handler.notifier.Notify(evalContext) @@ -103,8 +103,8 @@ func (handler *DefaultResultHandler) shouldUpdateAlertState(evalContext *EvalCon func countStateResult(state m.AlertStateType) { switch state { - case m.AlertStateInitialized: - metrics.M_Alerting_Result_State_Initialized.Inc(1) + case m.AlertStatePending: + metrics.M_Alerting_Result_State_Pending.Inc(1) case m.AlertStateAlerting: metrics.M_Alerting_Result_State_Alerting.Inc(1) case m.AlertStateOK: diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index 4eca93a10d6..c7d7f341b61 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -173,7 +173,7 @@ func upsertAlerts(existingAlerts []*m.Alert, cmd *m.SaveAlertsCommand, sess *xor } else { alert.Updated = time.Now() alert.Created = time.Now() - alert.State = m.AlertStateInitialized + alert.State = m.AlertStatePending alert.NewStateDate = time.Now() _, err := sess.Insert(alert) diff --git a/pkg/services/sqlstore/alert_test.go b/pkg/services/sqlstore/alert_test.go index 3aba605bb5c..e22b1c48c47 100644 --- a/pkg/services/sqlstore/alert_test.go +++ b/pkg/services/sqlstore/alert_test.go @@ -47,7 +47,7 @@ func TestAlertingDataAccess(t *testing.T) { So(err2, ShouldBeNil) So(alert.Name, ShouldEqual, "Alerting title") So(alert.Message, ShouldEqual, "Alerting message") - So(alert.State, ShouldEqual, "initialized") + So(alert.State, ShouldEqual, "pending") So(alert.Frequency, ShouldEqual, 1) }) @@ -77,7 +77,7 @@ func TestAlertingDataAccess(t *testing.T) { So(query.Result[0].Name, ShouldEqual, "Name") Convey("Alert state should not be updated", func() { - So(query.Result[0].State, ShouldEqual, "initialized") + So(query.Result[0].State, ShouldEqual, "pending") }) }) From 7490c49f60ba5e90a748bf95c78788a102b49291 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 1 Nov 2016 07:58:38 -0700 Subject: [PATCH 10/34] Alert un paused to pending state --- pkg/api/alerting.go | 2 +- pkg/services/sqlstore/alert.go | 2 +- public/app/features/alerting/alert_def.ts | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index e745f820aec..56c02baa744 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -264,7 +264,7 @@ func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response { return ApiError(500, "", err) } - var response models.AlertStateType = models.AlertStateNoData + var response models.AlertStateType = models.AlertStatePending pausedState := "un paused" if cmd.Paused { response = models.AlertStatePaused diff --git a/pkg/services/sqlstore/alert.go b/pkg/services/sqlstore/alert.go index c7d7f341b61..44693f8d474 100644 --- a/pkg/services/sqlstore/alert.go +++ b/pkg/services/sqlstore/alert.go @@ -260,7 +260,7 @@ func PauseAlertRule(cmd *m.PauseAlertCommand) error { if cmd.Paused { newState = m.AlertStatePaused } else { - newState = m.AlertStateNoData + newState = m.AlertStatePending } alert.State = newState diff --git a/public/app/features/alerting/alert_def.ts b/public/app/features/alerting/alert_def.ts index 9c567d2ebc5..8696e593074 100644 --- a/public/app/features/alerting/alert_def.ts +++ b/public/app/features/alerting/alert_def.ts @@ -87,6 +87,13 @@ function getStateDisplayModel(state) { stateClass: 'alert-state-paused' }; } + case 'pending': { + return { + text: 'PENDING', + iconClass: "fa fa-exclamation", + stateClass: 'alert-state-warning' + }; + } } } From 9b28bf25a40198b42d32401d5db736276d825886 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 1 Nov 2016 18:35:15 +0100 Subject: [PATCH 11/34] docs(cli): add info about how to install specific plugin version closes #6434 --- docs/sources/plugins/installation.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/sources/plugins/installation.md b/docs/sources/plugins/installation.md index ff4f51729af..5880d18c0e3 100644 --- a/docs/sources/plugins/installation.md +++ b/docs/sources/plugins/installation.md @@ -34,11 +34,16 @@ List available plugins grafana-cli plugins list-remote ``` -Install a plugin type +Install the latest version of a plugin ``` grafana-cli plugins install ``` +Install a specific version of a plugin +``` +grafana-cli plugins install +``` + List installed plugins ``` grafana-cli plugins ls From 1e8beb89831b66cb5d9e0b92e3ff173096cb75f7 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Tue, 1 Nov 2016 17:55:45 -0700 Subject: [PATCH 12/34] Fixed intervalFormat for Graphite Alerting --- pkg/tsdb/graphite/graphite.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index a2783a107fe..10eb044c5bd 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -11,6 +11,7 @@ import ( "path" "strings" "time" + "regexp" "golang.org/x/net/context/ctxhttp" @@ -58,9 +59,9 @@ func (e *GraphiteExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, for _, query := range queries { if fullTarget, err := query.Model.Get("targetFull").String(); err == nil { - formData["target"] = []string{fullTarget} + formData["target"] = []string{fixIntervalFormat(fullTarget)} } else { - formData["target"] = []string{query.Model.Get("target").MustString()} + formData["target"] = []string{fixIntervalFormat(query.Model.Get("target").MustString())} } } @@ -150,3 +151,17 @@ func formatTimeRange(input string) string { } return strings.Replace(strings.Replace(input, "m", "min", -1), "M", "mon", -1) } + +func fixIntervalFormat(target string) string { + rMinute := regexp.MustCompile("'(\\d+)m'") + rMin := regexp.MustCompile("m") + target = rMinute.ReplaceAllStringFunc(target, func(m string) string { + return rMin.ReplaceAllString(m, "min") + }) + rMonth := regexp.MustCompile("'(\\d+)M'") + rMon := regexp.MustCompile("M") + target = rMonth.ReplaceAllStringFunc(target, func(M string) string { + return rMon.ReplaceAllString(M, "mon") + }) + return target +} From 2088363bf9979ca55879d6ab99736182d6731ac6 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 2 Nov 2016 00:00:18 -0700 Subject: [PATCH 13/34] Added tests for graphite alerting --- pkg/tsdb/graphite/graphite_test.go | 46 ++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pkg/tsdb/graphite/graphite_test.go b/pkg/tsdb/graphite/graphite_test.go index 7a3bba9035b..f953badb17a 100644 --- a/pkg/tsdb/graphite/graphite_test.go +++ b/pkg/tsdb/graphite/graphite_test.go @@ -1 +1,47 @@ package graphite + +import ( + . "github.com/smartystreets/goconvey/convey" + "testing" +) + +func TestGraphiteFunctions(t *testing.T) { + Convey("Testing Graphite Executor", t, func() { + + Convey("formatting time range for now", func() { + + timeRange := formatTimeRange("now") + So(timeRange, ShouldEqual, "now") + + }) + + Convey("formatting time range for now-1m", func() { + + timeRange := formatTimeRange("now-1m") + So(timeRange, ShouldEqual, "now-1min") + + }) + + Convey("formatting time range for now-1M", func() { + + timeRange := formatTimeRange("now-1M") + So(timeRange, ShouldEqual, "now-1mon") + + }) + + Convey("fix interval format in query for 1m", func() { + + timeRange := formatTimeRange("aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1m'), 4)") + So(timeRange, ShouldEqual, "aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1min'), 4)") + + }) + + Convey("fix interval format in query for 1M", func() { + + timeRange := formatTimeRange("aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1M'), 4)") + So(timeRange, ShouldEqual, "aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1mon'), 4)") + + }) + + }) +} From d4bc92b2679b38b79e105aecb906c7655d2ee5d7 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 2 Nov 2016 08:51:34 +0100 Subject: [PATCH 14/34] feat(tsdb): default tsdb httpclient --- pkg/tsdb/graphite/graphite.go | 11 +-- pkg/tsdb/http.go | 29 ++++++++ pkg/tsdb/influxdb/influxdb.go | 11 +-- pkg/tsdb/opentsdb/opentsdb.go | 113 ++++++++++++++---------------- pkg/tsdb/prometheus/prometheus.go | 4 +- 5 files changed, 84 insertions(+), 84 deletions(-) create mode 100644 pkg/tsdb/http.go diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index a2783a107fe..89cea55938f 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -2,7 +2,6 @@ package graphite import ( "context" - "crypto/tls" "encoding/json" "fmt" "io/ioutil" @@ -10,7 +9,6 @@ import ( "net/url" "path" "strings" - "time" "golang.org/x/net/context/ctxhttp" @@ -36,14 +34,7 @@ func init() { glog = log.New("tsdb.graphite") tsdb.RegisterExecutor("graphite", NewGraphiteExecutor) - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - - HttpClient = &http.Client{ - Timeout: time.Duration(15 * time.Second), - Transport: tr, - } + HttpClient = tsdb.GetDefaultClient() } func (e *GraphiteExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult { diff --git a/pkg/tsdb/http.go b/pkg/tsdb/http.go new file mode 100644 index 00000000000..f5de146d470 --- /dev/null +++ b/pkg/tsdb/http.go @@ -0,0 +1,29 @@ +package tsdb + +import ( + "crypto/tls" + "net" + "net/http" + "time" +) + +func GetDefaultClient() *http.Client { + tr := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + }).DialContext, + MaxIdleConns: 100, + IdleConnTimeout: 90 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + + return &http.Client{ + Timeout: time.Duration(30 * time.Second), + Transport: tr, + } +} diff --git a/pkg/tsdb/influxdb/influxdb.go b/pkg/tsdb/influxdb/influxdb.go index b546a6ee3a9..5e5b893ad23 100644 --- a/pkg/tsdb/influxdb/influxdb.go +++ b/pkg/tsdb/influxdb/influxdb.go @@ -2,13 +2,11 @@ package influxdb import ( "context" - "crypto/tls" "encoding/json" "fmt" "net/http" "net/url" "path" - "time" "golang.org/x/net/context/ctxhttp" @@ -41,14 +39,7 @@ func init() { glog = log.New("tsdb.influxdb") tsdb.RegisterExecutor("influxdb", NewInfluxDBExecutor) - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - - HttpClient = &http.Client{ - Timeout: time.Duration(15 * time.Second), - Transport: tr, - } + HttpClient = tsdb.GetDefaultClient() } func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult { diff --git a/pkg/tsdb/opentsdb/opentsdb.go b/pkg/tsdb/opentsdb/opentsdb.go index 3ecd52ca723..c5e5b0020d1 100644 --- a/pkg/tsdb/opentsdb/opentsdb.go +++ b/pkg/tsdb/opentsdb/opentsdb.go @@ -2,19 +2,17 @@ package opentsdb import ( "context" - "crypto/tls" "fmt" "path" "strconv" "strings" - "time" "golang.org/x/net/context/ctxhttp" + "encoding/json" "io/ioutil" "net/http" "net/url" - "encoding/json" "gopkg.in/guregu/null.v3" @@ -40,14 +38,7 @@ func init() { plog = log.New("tsdb.opentsdb") tsdb.RegisterExecutor("opentsdb", NewOpenTsdbExecutor) - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - - HttpClient = &http.Client{ - Timeout: time.Duration(15 * time.Second), - Transport: tr, - } + HttpClient = tsdb.GetDefaultClient() } func (e *OpenTsdbExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, queryContext *tsdb.QueryContext) *tsdb.BatchResult { @@ -58,9 +49,9 @@ func (e *OpenTsdbExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice, tsdbQuery.Start = queryContext.TimeRange.GetFromAsMsEpoch() tsdbQuery.End = queryContext.TimeRange.GetToAsMsEpoch() - for _ , query := range queries { - metric := e.buildMetric(query) - tsdbQuery.Queries = append(tsdbQuery.Queries, metric) + for _, query := range queries { + metric := e.buildMetric(query) + tsdbQuery.Queries = append(tsdbQuery.Queries, metric) } if setting.Env == setting.DEV { @@ -104,7 +95,7 @@ func (e *OpenTsdbExecutor) createRequest(data OpenTsdbQuery) (*http.Request, err if e.BasicAuth { req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword) } - + return req, err } @@ -152,61 +143,61 @@ func (e *OpenTsdbExecutor) parseResponse(query OpenTsdbQuery, res *http.Response return queryResults, nil } -func (e *OpenTsdbExecutor) buildMetric(query *tsdb.Query) (map[string]interface{}) { +func (e *OpenTsdbExecutor) buildMetric(query *tsdb.Query) map[string]interface{} { metric := make(map[string]interface{}) - // Setting metric and aggregator - metric["metric"] = query.Model.Get("metric").MustString() - metric["aggregator"] = query.Model.Get("aggregator").MustString() + // Setting metric and aggregator + metric["metric"] = query.Model.Get("metric").MustString() + metric["aggregator"] = query.Model.Get("aggregator").MustString() - // Setting downsampling options - disableDownsampling := query.Model.Get("disableDownsampling").MustBool() - if !disableDownsampling { - downsampleInterval := query.Model.Get("downsampleInterval").MustString() - if downsampleInterval == "" { - downsampleInterval = "1m" //default value for blank - } - downsample := downsampleInterval + "-" + query.Model.Get("downsampleAggregator").MustString() - if query.Model.Get("downsampleFillPolicy").MustString() != "none" { - metric["downsample"] = downsample + "-" + query.Model.Get("downsampleFillPolicy").MustString() - } else { - metric["downsample"] = downsample - } + // Setting downsampling options + disableDownsampling := query.Model.Get("disableDownsampling").MustBool() + if !disableDownsampling { + downsampleInterval := query.Model.Get("downsampleInterval").MustString() + if downsampleInterval == "" { + downsampleInterval = "1m" //default value for blank + } + downsample := downsampleInterval + "-" + query.Model.Get("downsampleAggregator").MustString() + if query.Model.Get("downsampleFillPolicy").MustString() != "none" { + metric["downsample"] = downsample + "-" + query.Model.Get("downsampleFillPolicy").MustString() + } else { + metric["downsample"] = downsample + } + } + + // Setting rate options + if query.Model.Get("shouldComputeRate").MustBool() { + + metric["rate"] = true + rateOptions := make(map[string]interface{}) + rateOptions["counter"] = query.Model.Get("isCounter").MustBool() + + counterMax, counterMaxCheck := query.Model.CheckGet("counterMax") + if counterMaxCheck { + rateOptions["counterMax"] = counterMax.MustFloat64() } - // Setting rate options - if query.Model.Get("shouldComputeRate").MustBool() { - - metric["rate"] = true - rateOptions := make(map[string]interface{}) - rateOptions["counter"] = query.Model.Get("isCounter").MustBool() - - counterMax, counterMaxCheck := query.Model.CheckGet("counterMax") - if counterMaxCheck { - rateOptions["counterMax"] = counterMax.MustFloat64() - } - - resetValue, resetValueCheck := query.Model.CheckGet("counterResetValue") - if resetValueCheck { - rateOptions["resetValue"] = resetValue.MustFloat64() - } - - metric["rateOptions"] = rateOptions + resetValue, resetValueCheck := query.Model.CheckGet("counterResetValue") + if resetValueCheck { + rateOptions["resetValue"] = resetValue.MustFloat64() } - // Setting tags - tags, tagsCheck := query.Model.CheckGet("tags") - if tagsCheck && len(tags.MustMap()) > 0 { - metric["tags"] = tags.MustMap() - } + metric["rateOptions"] = rateOptions + } - // Setting filters - filters, filtersCheck := query.Model.CheckGet("filters") - if filtersCheck && len(filters.MustArray()) > 0 { - metric["filters"] = filters.MustArray() - } + // Setting tags + tags, tagsCheck := query.Model.CheckGet("tags") + if tagsCheck && len(tags.MustMap()) > 0 { + metric["tags"] = tags.MustMap() + } - return metric + // Setting filters + filters, filtersCheck := query.Model.CheckGet("filters") + if filtersCheck && len(filters.MustArray()) > 0 { + metric["filters"] = filters.MustArray() + } + + return metric } diff --git a/pkg/tsdb/prometheus/prometheus.go b/pkg/tsdb/prometheus/prometheus.go index 6dc4146ad0e..2ec03210279 100644 --- a/pkg/tsdb/prometheus/prometheus.go +++ b/pkg/tsdb/prometheus/prometheus.go @@ -3,7 +3,6 @@ package prometheus import ( "context" "fmt" - "net/http" "regexp" "strings" "time" @@ -25,8 +24,7 @@ func NewPrometheusExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor { } var ( - plog log.Logger - HttpClient http.Client + plog log.Logger ) func init() { From a4a2e35bb247417f68b5c2901908ac55bc559147 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 2 Nov 2016 02:07:08 -0700 Subject: [PATCH 15/34] Verified interval format function and gofmt check --- pkg/tsdb/graphite/graphite.go | 7 ++++--- pkg/tsdb/graphite/graphite_test.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 10eb044c5bd..f372079f991 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -9,9 +9,9 @@ import ( "net/http" "net/url" "path" + "regexp" "strings" "time" - "regexp" "golang.org/x/net/context/ctxhttp" @@ -153,15 +153,16 @@ func formatTimeRange(input string) string { } func fixIntervalFormat(target string) string { - rMinute := regexp.MustCompile("'(\\d+)m'") + rMinute := regexp.MustCompile(`'(\d+)m'`) rMin := regexp.MustCompile("m") target = rMinute.ReplaceAllStringFunc(target, func(m string) string { return rMin.ReplaceAllString(m, "min") }) - rMonth := regexp.MustCompile("'(\\d+)M'") + rMonth := regexp.MustCompile(`'(\d+)M'`) rMon := regexp.MustCompile("M") target = rMonth.ReplaceAllStringFunc(target, func(M string) string { return rMon.ReplaceAllString(M, "mon") }) + glog.Debug("Graphite Query", "target", target) return target } diff --git a/pkg/tsdb/graphite/graphite_test.go b/pkg/tsdb/graphite/graphite_test.go index f953badb17a..24b81b62af5 100644 --- a/pkg/tsdb/graphite/graphite_test.go +++ b/pkg/tsdb/graphite/graphite_test.go @@ -6,7 +6,7 @@ import ( ) func TestGraphiteFunctions(t *testing.T) { - Convey("Testing Graphite Executor", t, func() { + Convey("Testing Graphite Functions", t, func() { Convey("formatting time range for now", func() { From 33ee85ede989aa695b40efa48d2e10b7553f0e5f Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 2 Nov 2016 11:11:06 -0700 Subject: [PATCH 16/34] Commented strange behavior of tests --- pkg/tsdb/graphite/graphite_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkg/tsdb/graphite/graphite_test.go b/pkg/tsdb/graphite/graphite_test.go index 24b81b62af5..f3b03a8b205 100644 --- a/pkg/tsdb/graphite/graphite_test.go +++ b/pkg/tsdb/graphite/graphite_test.go @@ -43,5 +43,21 @@ func TestGraphiteFunctions(t *testing.T) { }) + /* + Convey("should not override query", func() { + + timeRange := formatTimeRange("app.grafana.*.dashboards.views.1M.count") + So(timeRange, ShouldEqual, "app.grafana.*.dashboards.views.1M.count") + + }) + + Convey("should not override query", func() { + + timeRange := formatTimeRange("app.grafana.*.dashboards.views.1m.count") + So(timeRange, ShouldEqual, "app.grafana.*.dashboards.views.1m.count") + + }) + + */ }) } From 2b28cf1ff167208d4d573fd2ea72aa407025817c Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 2 Nov 2016 12:42:27 -0700 Subject: [PATCH 17/34] Fixed the tooltip message for slack notifier --- public/app/features/alerting/partials/notification_edit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/alerting/partials/notification_edit.html b/public/app/features/alerting/partials/notification_edit.html index ede9cc247dd..47999b5289d 100644 --- a/public/app/features/alerting/partials/notification_edit.html +++ b/public/app/features/alerting/partials/notification_edit.html @@ -71,7 +71,7 @@ data-placement="right"> - Override default channel or user, use #channel-name or <@username></@username> + Override default channel or user, use #channel-name or @username
From d1d7c240f9f5281af917d6679bf4585e8db47094 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 2 Nov 2016 22:11:59 +0100 Subject: [PATCH 18/34] tech(mailer): dont use deprecated method --- pkg/services/notifications/mailer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/notifications/mailer.go b/pkg/services/notifications/mailer.go index 9e54e906d99..4bcce804ee4 100644 --- a/pkg/services/notifications/mailer.go +++ b/pkg/services/notifications/mailer.go @@ -99,7 +99,7 @@ func createDialer() (*gomail.Dialer, error) { tlsconfig.Certificates = []tls.Certificate{cert} } - d := gomail.NewPlainDialer(host, iPort, setting.Smtp.User, setting.Smtp.Password) + d := gomail.NewDialer(host, iPort, setting.Smtp.User, setting.Smtp.Password) d.TLSConfig = tlsconfig return d, nil } From 942de5497ec4a899c575c4d9035ed4a5ca7c3673 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 2 Nov 2016 18:13:11 -0700 Subject: [PATCH 19/34] Fixed alert message typo --- pkg/services/alerting/extractor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go index d78e84f6974..eb452b3ba15 100644 --- a/pkg/services/alerting/extractor.go +++ b/pkg/services/alerting/extractor.go @@ -104,7 +104,7 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) { panelQuery := findPanelQueryByRefId(panel, queryRefId) if panelQuery == nil { - return nil, ValidationError{Reason: "Alert refes to query that cannot be found"} + return nil, ValidationError{Reason: "Alert refers to query that cannot be found"} } dsName := "" From d009c6b953610d0acd4d7e86b167f45d1471d893 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 2 Nov 2016 18:22:24 -0700 Subject: [PATCH 20/34] Log bad alert config --- pkg/services/alerting/extractor.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go index eb452b3ba15..3ab1c32972a 100644 --- a/pkg/services/alerting/extractor.go +++ b/pkg/services/alerting/extractor.go @@ -104,6 +104,7 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) { panelQuery := findPanelQueryByRefId(panel, queryRefId) if panelQuery == nil { + e.log.Error("Query not found", "panel", alert.PanelId, "queryRefId", queryRefId) return nil, ValidationError{Reason: "Alert refers to query that cannot be found"} } From fc91231104d3e7171eccd271f3932833618090e1 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Wed, 2 Nov 2016 22:31:59 -0700 Subject: [PATCH 21/34] Fixed failing tests --- pkg/tsdb/graphite/graphite.go | 1 - pkg/tsdb/graphite/graphite_test.go | 22 ++++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index f372079f991..a4c3cd8d947 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -163,6 +163,5 @@ func fixIntervalFormat(target string) string { target = rMonth.ReplaceAllStringFunc(target, func(M string) string { return rMon.ReplaceAllString(M, "mon") }) - glog.Debug("Graphite Query", "target", target) return target } diff --git a/pkg/tsdb/graphite/graphite_test.go b/pkg/tsdb/graphite/graphite_test.go index f3b03a8b205..c1a2736293b 100644 --- a/pkg/tsdb/graphite/graphite_test.go +++ b/pkg/tsdb/graphite/graphite_test.go @@ -31,33 +31,31 @@ func TestGraphiteFunctions(t *testing.T) { Convey("fix interval format in query for 1m", func() { - timeRange := formatTimeRange("aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1m'), 4)") + timeRange := fixIntervalFormat("aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1m'), 4)") So(timeRange, ShouldEqual, "aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1min'), 4)") }) Convey("fix interval format in query for 1M", func() { - timeRange := formatTimeRange("aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1M'), 4)") + timeRange := fixIntervalFormat("aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1M'), 4)") So(timeRange, ShouldEqual, "aliasByNode(hitcount(averageSeries(app.grafana.*.dashboards.views.count), '1mon'), 4)") }) - /* - Convey("should not override query", func() { + Convey("should not override query for 1M", func() { - timeRange := formatTimeRange("app.grafana.*.dashboards.views.1M.count") - So(timeRange, ShouldEqual, "app.grafana.*.dashboards.views.1M.count") + timeRange := fixIntervalFormat("app.grafana.*.dashboards.views.1M.count") + So(timeRange, ShouldEqual, "app.grafana.*.dashboards.views.1M.count") - }) + }) - Convey("should not override query", func() { + Convey("should not override query for 1m", func() { - timeRange := formatTimeRange("app.grafana.*.dashboards.views.1m.count") - So(timeRange, ShouldEqual, "app.grafana.*.dashboards.views.1m.count") + timeRange := fixIntervalFormat("app.grafana.*.dashboards.views.1m.count") + So(timeRange, ShouldEqual, "app.grafana.*.dashboards.views.1m.count") - }) + }) - */ }) } From ff5d4e8e0c8fd83243cc4131a409d19ae42d9084 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 3 Nov 2016 07:14:34 +0100 Subject: [PATCH 22/34] fix(alerting): temp fix for broken AND condition This should be refactored. lets return condition results instead of setting new value on the evalContext. The condition execution should only be able to update its own state. closes #6449 --- pkg/services/alerting/conditions/query.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go index a9a99ba919e..7cf255c6af8 100644 --- a/pkg/services/alerting/conditions/query.go +++ b/pkg/services/alerting/conditions/query.go @@ -42,6 +42,7 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) { } emptySerieCount := 0 + evalMatchCount := 0 for _, series := range seriesList { reducedValue := c.Reducer.Reduce(series) evalMatch := c.Evaluator.Eval(reducedValue) @@ -58,6 +59,7 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) { } if evalMatch { + evalMatchCount++ context.EvalMatches = append(context.EvalMatches, &alerting.EvalMatch{ Metric: series.Name, Value: reducedValue.Float64, @@ -66,7 +68,7 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) { } context.NoDataFound = emptySerieCount == len(seriesList) - context.Firing = len(context.EvalMatches) > 0 + context.Firing = evalMatchCount > 0 } func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *tsdb.TimeRange) (tsdb.TimeSeriesSlice, error) { From c138b04c5e07660a2cc38f284c9cc8160e384dd1 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 3 Nov 2016 07:25:00 +0100 Subject: [PATCH 23/34] feat(alerting): avoid double logging --- pkg/services/alerting/extractor.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go index 3ab1c32972a..786fae846ad 100644 --- a/pkg/services/alerting/extractor.go +++ b/pkg/services/alerting/extractor.go @@ -3,6 +3,8 @@ package alerting import ( "errors" + "fmt" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/log" @@ -104,8 +106,8 @@ func (e *DashAlertExtractor) GetAlerts() ([]*m.Alert, error) { panelQuery := findPanelQueryByRefId(panel, queryRefId) if panelQuery == nil { - e.log.Error("Query not found", "panel", alert.PanelId, "queryRefId", queryRefId) - return nil, ValidationError{Reason: "Alert refers to query that cannot be found"} + reason := fmt.Sprintf("Alert on PanelId: %v refers to query(%s) that cannot be found", alert.PanelId, queryRefId) + return nil, ValidationError{Reason: reason} } dsName := "" From 849ac9441acab8d64b05d641407b6bbdc6f1a038 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Thu, 3 Nov 2016 01:18:01 -0700 Subject: [PATCH 24/34] Fixed multi-value nested templating for opentsdb --- .../templating/specs/template_srv_specs.ts | 5 +++++ public/app/features/templating/templateSrv.js | 14 ++++++++++++++ .../app/plugins/datasource/opentsdb/datasource.js | 2 +- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/public/app/features/templating/specs/template_srv_specs.ts b/public/app/features/templating/specs/template_srv_specs.ts index 94b1e211293..ca336e97ba3 100644 --- a/public/app/features/templating/specs/template_srv_specs.ts +++ b/public/app/features/templating/specs/template_srv_specs.ts @@ -145,6 +145,11 @@ describe('templateSrv', function() { expect(result).to.be('test|test2'); }); + it('multi value and distributed should render distributed string', function() { + var result = _templateSrv.formatValue(['test','test2'], 'distributed', { name: 'build' }); + expect(result).to.be('test,build=test2'); + }); + it('slash should be properly escaped in regex format', function() { var result = _templateSrv.formatValue('Gi3/14', 'regex'); expect(result).to.be('Gi3\\/14'); diff --git a/public/app/features/templating/templateSrv.js b/public/app/features/templating/templateSrv.js index dadb8f23a89..e4627ac6165 100644 --- a/public/app/features/templating/templateSrv.js +++ b/public/app/features/templating/templateSrv.js @@ -95,6 +95,9 @@ function (angular, _, kbn) { } return value.join('|'); } + case "distributed": { + return this.distributeVariable(value, variable.name); + } default: { if (typeof value === 'string') { return value; @@ -210,6 +213,17 @@ function (angular, _, kbn) { }); }; + this.distributeVariable = function(value, variable) { + value = _.map(value, function(val, index) { + if (index !== 0) { + return variable + "=" + val; + } else { + return val; + } + }); + return value.join(','); + }; + }); }); diff --git a/public/app/plugins/datasource/opentsdb/datasource.js b/public/app/plugins/datasource/opentsdb/datasource.js index 4ef7c9761fe..873ab4255a1 100644 --- a/public/app/plugins/datasource/opentsdb/datasource.js +++ b/public/app/plugins/datasource/opentsdb/datasource.js @@ -244,7 +244,7 @@ function (angular, _, dateMath) { var interpolated; try { - interpolated = templateSrv.replace(query); + interpolated = templateSrv.replace(query, {}, "distributed"); } catch (err) { return $q.reject(err); From 89cf32b16069756bd5279c0f4169f09e78c542b0 Mon Sep 17 00:00:00 2001 From: utkarshcmu Date: Thu, 3 Nov 2016 01:22:24 -0700 Subject: [PATCH 25/34] Made quotes consistent --- public/app/plugins/datasource/opentsdb/datasource.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/plugins/datasource/opentsdb/datasource.js b/public/app/plugins/datasource/opentsdb/datasource.js index 873ab4255a1..76ee763cafc 100644 --- a/public/app/plugins/datasource/opentsdb/datasource.js +++ b/public/app/plugins/datasource/opentsdb/datasource.js @@ -244,7 +244,7 @@ function (angular, _, dateMath) { var interpolated; try { - interpolated = templateSrv.replace(query, {}, "distributed"); + interpolated = templateSrv.replace(query, {}, 'distributed'); } catch (err) { return $q.reject(err); From 61c48aecc233e0c8b0ad72ba6bbebfe748c3a129 Mon Sep 17 00:00:00 2001 From: Carl Bergquist Date: Thu, 3 Nov 2016 11:01:21 +0100 Subject: [PATCH 26/34] [Alertlist] Set max-height to respect row height (#6454) * feat(alertlist): max-height to respect row height closes #6417 * feat(alertlist): use pre calculated panel.height instead * style(alertlist): rearrange variable order --- public/app/plugins/panel/alertlist/editor.html | 8 -------- public/app/plugins/panel/alertlist/module.html | 2 +- public/app/plugins/panel/alertlist/module.ts | 3 +++ public/sass/_grafana.scss | 1 + public/sass/components/_panel_alertlist.scss | 3 +++ 5 files changed, 8 insertions(+), 9 deletions(-) create mode 100644 public/sass/components/_panel_alertlist.scss diff --git a/public/app/plugins/panel/alertlist/editor.html b/public/app/plugins/panel/alertlist/editor.html index b75a7e453aa..b2038df34b8 100644 --- a/public/app/plugins/panel/alertlist/editor.html +++ b/public/app/plugins/panel/alertlist/editor.html @@ -20,12 +20,4 @@
- -
- -
- -
- -
diff --git a/public/app/plugins/panel/alertlist/module.html b/public/app/plugins/panel/alertlist/module.html index 13a32f1eb1e..2940e2ae33e 100644 --- a/public/app/plugins/panel/alertlist/module.html +++ b/public/app/plugins/panel/alertlist/module.html @@ -1,4 +1,4 @@ -
+
  1. diff --git a/public/app/plugins/panel/alertlist/module.ts b/public/app/plugins/panel/alertlist/module.ts index 96d7ad18e7c..543c5f04833 100644 --- a/public/app/plugins/panel/alertlist/module.ts +++ b/public/app/plugins/panel/alertlist/module.ts @@ -17,6 +17,7 @@ class AlertListPanel extends PanelCtrl { {text: 'Recent state changes', value: 'changes'} ]; + contentHeight: string; stateFilter: any = {}; currentAlerts: any = []; alertHistory: any = []; @@ -27,6 +28,7 @@ class AlertListPanel extends PanelCtrl { stateFilter: [] }; + /** @ngInject */ constructor($scope, $injector, private $location, private backendSrv, private timeSrv, private templateSrv) { super($scope, $injector); @@ -55,6 +57,7 @@ class AlertListPanel extends PanelCtrl { } onRender() { + this.contentHeight = "max-height: " + this.height + "px;"; if (this.panel.show === 'current') { this.getCurrentAlertState(); } diff --git a/public/sass/_grafana.scss b/public/sass/_grafana.scss index 936ce0af4d6..60f0797dda4 100644 --- a/public/sass/_grafana.scss +++ b/public/sass/_grafana.scss @@ -41,6 +41,7 @@ @import "components/tags"; @import "components/panel_graph"; @import "components/submenu"; +@import "components/panel_alertlist"; @import "components/panel_dashlist"; @import "components/panel_pluginlist"; @import "components/panel_singlestat"; diff --git a/public/sass/components/_panel_alertlist.scss b/public/sass/components/_panel_alertlist.scss new file mode 100644 index 00000000000..41f63b61bd4 --- /dev/null +++ b/public/sass/components/_panel_alertlist.scss @@ -0,0 +1,3 @@ +.panel-alert-list { + overflow-y: scroll; +} From 4af420f759a47305f87f6eee067195b2ec3a7bfc Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 3 Nov 2016 15:26:17 +0100 Subject: [PATCH 27/34] tech(alerting): refactor how evalhandler uses conditions --- pkg/services/alerting/conditions/query.go | 17 +++++--- .../alerting/conditions/query_test.go | 40 +++++++++---------- pkg/services/alerting/eval_handler.go | 12 +++++- pkg/services/alerting/eval_handler_test.go | 11 ++--- pkg/services/alerting/interfaces.go | 8 +++- pkg/services/alerting/rule_test.go | 4 +- 6 files changed, 57 insertions(+), 35 deletions(-) diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go index 7cf255c6af8..b73db9d590e 100644 --- a/pkg/services/alerting/conditions/query.go +++ b/pkg/services/alerting/conditions/query.go @@ -33,16 +33,17 @@ type AlertQuery struct { To string } -func (c *QueryCondition) Eval(context *alerting.EvalContext) { +func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) { timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To) + seriesList, err := c.executeQuery(context, timeRange) if err != nil { - context.Error = err - return + return nil, err } emptySerieCount := 0 evalMatchCount := 0 + var matches []*alerting.EvalMatch for _, series := range seriesList { reducedValue := c.Reducer.Reduce(series) evalMatch := c.Evaluator.Eval(reducedValue) @@ -60,15 +61,19 @@ func (c *QueryCondition) Eval(context *alerting.EvalContext) { if evalMatch { evalMatchCount++ - context.EvalMatches = append(context.EvalMatches, &alerting.EvalMatch{ + + matches = append(matches, &alerting.EvalMatch{ Metric: series.Name, Value: reducedValue.Float64, }) } } - context.NoDataFound = emptySerieCount == len(seriesList) - context.Firing = evalMatchCount > 0 + return &alerting.ConditionResult{ + Firing: evalMatchCount > 0, + NoDataFound: emptySerieCount == len(seriesList), + EvalMatches: matches, + }, nil } func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *tsdb.TimeRange) (tsdb.TimeSeriesSlice, error) { diff --git a/pkg/services/alerting/conditions/query_test.go b/pkg/services/alerting/conditions/query_test.go index 43e0381a80c..c3797beaf37 100644 --- a/pkg/services/alerting/conditions/query_test.go +++ b/pkg/services/alerting/conditions/query_test.go @@ -46,19 +46,19 @@ func TestQueryCondition(t *testing.T) { Convey("should fire when avg is above 100", func() { points := tsdb.NewTimeSeriesPointsFromArgs(120, 0) ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", points)} - ctx.exec() + cr, err := ctx.exec() - So(ctx.result.Error, ShouldBeNil) - So(ctx.result.Firing, ShouldBeTrue) + So(err, ShouldBeNil) + So(cr.Firing, ShouldBeTrue) }) Convey("Should not fire when avg is below 100", func() { points := tsdb.NewTimeSeriesPointsFromArgs(90, 0) ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", points)} - ctx.exec() + cr, err := ctx.exec() - So(ctx.result.Error, ShouldBeNil) - So(ctx.result.Firing, ShouldBeFalse) + So(err, ShouldBeNil) + So(cr.Firing, ShouldBeFalse) }) Convey("Should fire if only first serie matches", func() { @@ -66,10 +66,10 @@ func TestQueryCondition(t *testing.T) { tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs(120, 0)), tsdb.NewTimeSeries("test2", tsdb.NewTimeSeriesPointsFromArgs(0, 0)), } - ctx.exec() + cr, err := ctx.exec() - So(ctx.result.Error, ShouldBeNil) - So(ctx.result.Firing, ShouldBeTrue) + So(err, ShouldBeNil) + So(cr.Firing, ShouldBeTrue) }) Convey("Empty series", func() { @@ -78,10 +78,10 @@ func TestQueryCondition(t *testing.T) { tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs()), tsdb.NewTimeSeries("test2", tsdb.NewTimeSeriesPointsFromArgs()), } - ctx.exec() + cr, err := ctx.exec() - So(ctx.result.Error, ShouldBeNil) - So(ctx.result.NoDataFound, ShouldBeTrue) + So(err, ShouldBeNil) + So(cr.NoDataFound, ShouldBeTrue) }) Convey("Should set NoDataFound both series contains null", func() { @@ -89,10 +89,10 @@ func TestQueryCondition(t *testing.T) { tsdb.NewTimeSeries("test1", tsdb.TimeSeriesPoints{tsdb.TimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}), tsdb.NewTimeSeries("test2", tsdb.TimeSeriesPoints{tsdb.TimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}), } - ctx.exec() + cr, err := ctx.exec() - So(ctx.result.Error, ShouldBeNil) - So(ctx.result.NoDataFound, ShouldBeTrue) + So(err, ShouldBeNil) + So(cr.NoDataFound, ShouldBeTrue) }) Convey("Should not set NoDataFound if one serie is empty", func() { @@ -100,10 +100,10 @@ func TestQueryCondition(t *testing.T) { tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs()), tsdb.NewTimeSeries("test2", tsdb.NewTimeSeriesPointsFromArgs(120, 0)), } - ctx.exec() + cr, err := ctx.exec() - So(ctx.result.Error, ShouldBeNil) - So(ctx.result.NoDataFound, ShouldBeFalse) + So(err, ShouldBeNil) + So(cr.NoDataFound, ShouldBeFalse) }) }) }) @@ -120,7 +120,7 @@ type queryConditionTestContext struct { type queryConditionScenarioFunc func(c *queryConditionTestContext) -func (ctx *queryConditionTestContext) exec() { +func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error) { jsonModel, err := simplejson.NewJson([]byte(`{ "type": "query", "query": { @@ -146,7 +146,7 @@ func (ctx *queryConditionTestContext) exec() { }, nil } - condition.Eval(ctx.result) + return condition.Eval(ctx.result) } func queryConditionScenario(desc string, fn queryConditionScenarioFunc) { diff --git a/pkg/services/alerting/eval_handler.go b/pkg/services/alerting/eval_handler.go index 74054ba8191..538c639abb8 100644 --- a/pkg/services/alerting/eval_handler.go +++ b/pkg/services/alerting/eval_handler.go @@ -20,8 +20,12 @@ func NewEvalHandler() *DefaultEvalHandler { } func (e *DefaultEvalHandler) Eval(context *EvalContext) { + firing := true for _, condition := range context.Rule.Conditions { - condition.Eval(context) + cr, err := condition.Eval(context) + if err != nil { + context.Error = err + } // break if condition could not be evaluated if context.Error != nil { @@ -29,11 +33,15 @@ func (e *DefaultEvalHandler) Eval(context *EvalContext) { } // break if result has not triggered yet - if context.Firing == false { + if cr.Firing == false { + firing = false break } + + context.EvalMatches = append(context.EvalMatches, cr.EvalMatches...) } + context.Firing = firing context.EndTime = time.Now() elapsedTime := context.EndTime.Sub(context.StartTime) / time.Millisecond metrics.M_Alerting_Exeuction_Time.Update(elapsedTime) diff --git a/pkg/services/alerting/eval_handler_test.go b/pkg/services/alerting/eval_handler_test.go index b69e62f9622..4c2ec24b506 100644 --- a/pkg/services/alerting/eval_handler_test.go +++ b/pkg/services/alerting/eval_handler_test.go @@ -8,11 +8,12 @@ import ( ) type conditionStub struct { - firing bool + firing bool + matches []*EvalMatch } -func (c *conditionStub) Eval(context *EvalContext) { - context.Firing = c.firing +func (c *conditionStub) Eval(context *EvalContext) (*ConditionResult, error) { + return &ConditionResult{Firing: c.firing, EvalMatches: c.matches}, nil } func TestAlertingExecutor(t *testing.T) { @@ -30,10 +31,10 @@ func TestAlertingExecutor(t *testing.T) { So(context.Firing, ShouldEqual, true) }) - Convey("Show return false with not passing condition", func() { + Convey("Show return false with not passing asdf", func() { context := NewEvalContext(context.TODO(), &Rule{ Conditions: []Condition{ - &conditionStub{firing: true}, + &conditionStub{firing: true, matches: []*EvalMatch{&EvalMatch{}, &EvalMatch{}}}, &conditionStub{firing: false}, }, }) diff --git a/pkg/services/alerting/interfaces.go b/pkg/services/alerting/interfaces.go index 583e12a120d..cc2561473e3 100644 --- a/pkg/services/alerting/interfaces.go +++ b/pkg/services/alerting/interfaces.go @@ -21,6 +21,12 @@ type Notifier interface { GetIsDefault() bool } +type ConditionResult struct { + Firing bool + NoDataFound bool + EvalMatches []*EvalMatch +} + type Condition interface { - Eval(result *EvalContext) + Eval(result *EvalContext) (*ConditionResult, error) } diff --git a/pkg/services/alerting/rule_test.go b/pkg/services/alerting/rule_test.go index 6144c01d54d..f8761efcd90 100644 --- a/pkg/services/alerting/rule_test.go +++ b/pkg/services/alerting/rule_test.go @@ -10,7 +10,9 @@ import ( type FakeCondition struct{} -func (f *FakeCondition) Eval(context *EvalContext) {} +func (f *FakeCondition) Eval(context *EvalContext) (*ConditionResult, error) { + return &ConditionResult{}, nil +} func TestAlertRuleModel(t *testing.T) { Convey("Testing alert rule", t, func() { From 0059beb85d098c2b080d64894874a8f34e9bc690 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 3 Nov 2016 17:34:57 +0100 Subject: [PATCH 28/34] chore(tsdb): remove commented code --- pkg/tsdb/prometheus/prometheus.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/tsdb/prometheus/prometheus.go b/pkg/tsdb/prometheus/prometheus.go index 2ec03210279..aad7e3f9428 100644 --- a/pkg/tsdb/prometheus/prometheus.go +++ b/pkg/tsdb/prometheus/prometheus.go @@ -156,9 +156,3 @@ func parseResponse(value pmodel.Value, query *PrometheusQuery) (map[string]*tsdb queryResults["A"] = queryRes return queryResults, nil } - -/* -func resultWithError(result *tsdb.BatchResult, err error) *tsdb.BatchResult { - result.Error = err - return result -}*/ From 1afe0e90f9fc6b3a2107d72fda28c99469ee035b Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 3 Nov 2016 18:04:09 +0100 Subject: [PATCH 29/34] fix(tsdb): fixes broken legend buidler for prometheus closes #6456 --- pkg/tsdb/prometheus/prometheus.go | 9 +++++---- pkg/tsdb/prometheus/prometheus_test.go | 13 +++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/pkg/tsdb/prometheus/prometheus.go b/pkg/tsdb/prometheus/prometheus.go index aad7e3f9428..ec5b098cbaf 100644 --- a/pkg/tsdb/prometheus/prometheus.go +++ b/pkg/tsdb/prometheus/prometheus.go @@ -81,6 +81,10 @@ func (e *PrometheusExecutor) Execute(ctx context.Context, queries tsdb.QuerySlic func formatLegend(metric pmodel.Metric, query *PrometheusQuery) string { reg, _ := regexp.Compile(`\{\{\s*(.+?)\s*\}\}`) + if query.LegendFormat == "" { + return metric.String() + } + result := reg.ReplaceAllFunc([]byte(query.LegendFormat), func(in []byte) []byte { labelName := strings.Replace(string(in), "{{", "", 1) labelName = strings.Replace(labelName, "}}", "", 1) @@ -108,10 +112,7 @@ func parseQuery(queries tsdb.QuerySlice, queryContext *tsdb.QueryContext) (*Prom return nil, err } - format, err := queryModel.Model.Get("legendFormat").String() - if err != nil { - return nil, err - } + format := queryModel.Model.Get("legendFormat").MustString("") start, err := queryContext.TimeRange.ParseFrom() if err != nil { diff --git a/pkg/tsdb/prometheus/prometheus_test.go b/pkg/tsdb/prometheus/prometheus_test.go index a4c38cae582..7d5ac939b32 100644 --- a/pkg/tsdb/prometheus/prometheus_test.go +++ b/pkg/tsdb/prometheus/prometheus_test.go @@ -22,5 +22,18 @@ func TestPrometheus(t *testing.T) { So(formatLegend(metric, query), ShouldEqual, "legend backend mobile {{broken}}") }) + + Convey("build full serie name", func() { + metric := map[p.LabelName]p.LabelValue{ + p.LabelName("app"): p.LabelValue("backend"), + p.LabelName("device"): p.LabelValue("mobile"), + } + + query := &PrometheusQuery{ + LegendFormat: "", + } + + So(formatLegend(metric, query), ShouldEqual, `http_request_total{app="backend", device="mobile"}`) + }) }) } From b578d06e4a8549a3454d151f8acfedb664fca2d9 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 3 Nov 2016 18:04:50 +0100 Subject: [PATCH 30/34] chore(notification): improve log message --- pkg/services/alerting/notifier.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/services/alerting/notifier.go b/pkg/services/alerting/notifier.go index 2017d9d7670..3fe7661b78c 100644 --- a/pkg/services/alerting/notifier.go +++ b/pkg/services/alerting/notifier.go @@ -49,7 +49,7 @@ func (n *RootNotifier) Notify(context *EvalContext) error { return err } - n.log.Info("Sending notifications for", "ruleId", context.Rule.Id, "Amount to send", len(notifiers)) + n.log.Info("Sending notifications for", "ruleId", context.Rule.Id, "sent count", len(notifiers)) if len(notifiers) == 0 { return nil From 2d7bb4a9f3ab03ea3ff4a71dd8ea384a2bcd8efd Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 3 Nov 2016 19:20:53 +0100 Subject: [PATCH 31/34] fix(tsdb): broken build :( --- pkg/tsdb/prometheus/prometheus_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/tsdb/prometheus/prometheus_test.go b/pkg/tsdb/prometheus/prometheus_test.go index 7d5ac939b32..d66ef75e479 100644 --- a/pkg/tsdb/prometheus/prometheus_test.go +++ b/pkg/tsdb/prometheus/prometheus_test.go @@ -25,8 +25,9 @@ func TestPrometheus(t *testing.T) { Convey("build full serie name", func() { metric := map[p.LabelName]p.LabelValue{ - p.LabelName("app"): p.LabelValue("backend"), - p.LabelName("device"): p.LabelValue("mobile"), + p.LabelName(p.MetricNameLabel): p.LabelValue("http_request_total"), + p.LabelName("app"): p.LabelValue("backend"), + p.LabelName("device"): p.LabelValue("mobile"), } query := &PrometheusQuery{ From f0d9d133c680c0dccda98f3adbcbae3cd91d9467 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 4 Nov 2016 07:55:09 +0100 Subject: [PATCH 32/34] tech(slack): format code using gofmt --- pkg/services/alerting/notifiers/slack.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/services/alerting/notifiers/slack.go b/pkg/services/alerting/notifiers/slack.go index 0e29af39e79..e7b6ab79456 100644 --- a/pkg/services/alerting/notifiers/slack.go +++ b/pkg/services/alerting/notifiers/slack.go @@ -27,16 +27,16 @@ func NewSlackNotifier(model *m.AlertNotification) (alerting.Notifier, error) { return &SlackNotifier{ NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings), Url: url, - Recipient: recipient, + Recipient: recipient, log: log.New("alerting.notifier.slack"), }, nil } type SlackNotifier struct { NotifierBase - Url string + Url string Recipient string - log log.Logger + log log.Logger } func (this *SlackNotifier) Notify(evalContext *alerting.EvalContext) error { From 05d24020b11fa39801d6b4f57f05548cc7adf5a6 Mon Sep 17 00:00:00 2001 From: TerraTech Date: Fri, 4 Nov 2016 03:41:12 -0700 Subject: [PATCH 33/34] settings.html: Fixup filename for custom.ini (#6463) --- public/app/features/admin/partials/settings.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/admin/partials/settings.html b/public/app/features/admin/partials/settings.html index 5e070f4d3c0..5371670a9ba 100644 --- a/public/app/features/admin/partials/settings.html +++ b/public/app/features/admin/partials/settings.html @@ -7,7 +7,7 @@
- These system settings are defined in grafana.ini or grafana.custom.ini (or overriden in ENV variables). + These system settings are defined in grafana.ini or custom.ini (or overriden in ENV variables). To change these you currently need to restart grafana.
From 7165c866c2cce14cb2f428bd6330360291667fca Mon Sep 17 00:00:00 2001 From: Ryan Bak Date: Fri, 4 Nov 2016 04:41:39 -0600 Subject: [PATCH 34/34] Add blank default options for constant templates (#6460) Restores ability to change constant templates without going into templating menu --- public/app/features/templating/constant_variable.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/public/app/features/templating/constant_variable.ts b/public/app/features/templating/constant_variable.ts index bf31dba96f9..59659459f85 100644 --- a/public/app/features/templating/constant_variable.ts +++ b/public/app/features/templating/constant_variable.ts @@ -16,6 +16,7 @@ export class ConstantVariable implements Variable { label: '', query: '', current: {}, + options: [], }; /** @ngInject **/