From 9c08d7aef5a35f967480e2bd531bd9b6dca93a7a Mon Sep 17 00:00:00 2001 From: cmartin0077 Date: Mon, 29 Aug 2016 04:03:54 -0700 Subject: [PATCH 01/29] fix(influxdb) fix tag value autocomplete with upper case values, fixes #5399 (#5919) --- public/app/core/directives/metric_segment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/core/directives/metric_segment.js b/public/app/core/directives/metric_segment.js index 4b3cd2e8de3..7669f2fb709 100644 --- a/public/app/core/directives/metric_segment.js +++ b/public/app/core/directives/metric_segment.js @@ -113,7 +113,7 @@ function (_, $, coreModule) { if (str[0] === '/') { str = str.substring(1); } if (str[str.length - 1] === '/') { str = str.substring(0, str.length-1); } try { - return item.toLowerCase().match(str); + return item.toLowerCase().match(str.toLowerCase()); } catch(e) { return false; } From 0050707134030b254b830141d763d3d08a4f1294 Mon Sep 17 00:00:00 2001 From: chrismartin0077 Date: Mon, 29 Aug 2016 05:45:28 -0700 Subject: [PATCH 02/29] feat(api) HEAD response, fixes #3854 --- pkg/api/api.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/api/api.go b/pkg/api/api.go index 31184a5a095..25158586822 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -19,6 +19,9 @@ func Register(r *macaron.Macaron) { quota := middleware.Quota bind := binding.Bind + // automatically set HEAD for every GET + r.SetAutoHead(true) + // not logged in views r.Get("/", reqSignedIn, Index) r.Get("/logout", Logout) From c0697b99d611c79b103e389fe1bc64929f913774 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 29 Aug 2016 15:49:25 +0200 Subject: [PATCH 03/29] fix(alerting): adds support for basic auth closes #5897 --- pkg/services/alerting/conditions/query.go | 14 ++++++++++---- pkg/tsdb/graphite/graphite.go | 9 +++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/pkg/services/alerting/conditions/query.go b/pkg/services/alerting/conditions/query.go index d94c2d468f2..0a661e8aaab 100644 --- a/pkg/services/alerting/conditions/query.go +++ b/pkg/services/alerting/conditions/query.go @@ -106,10 +106,16 @@ func (c *QueryCondition) getRequestForAlertRule(datasource *m.DataSource) *tsdb. RefId: "A", Query: c.Query.Model.Get("target").MustString(), DataSource: &tsdb.DataSourceInfo{ - Id: datasource.Id, - Name: datasource.Name, - PluginId: datasource.Type, - Url: datasource.Url, + Id: datasource.Id, + Name: datasource.Name, + PluginId: datasource.Type, + Url: datasource.Url, + User: datasource.User, + Password: datasource.Password, + Database: datasource.Database, + BasicAuth: datasource.BasicAuth, + BasicAuthUser: datasource.BasicAuthUser, + BasicAuthPassword: datasource.BasicAuthPassword, }, }, }, diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 2a68b23df71..68f92d7527e 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -43,12 +43,17 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC } client := http.Client{Timeout: time.Duration(10 * time.Second)} - res, err := client.PostForm(e.Url+"/render?", params) + req, _ := http.NewRequest(http.MethodPost, e.Url+"/render?", strings.NewReader(params.Encode())) + if e.BasicAuth { + req.SetBasicAuth("carl", "carl") + } + + res, err := client.Do(req) + defer res.Body.Close() if err != nil { result.Error = err return result } - defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { From 88ca69536193acb8c398028e9942f70bee088189 Mon Sep 17 00:00:00 2001 From: chrismartin0077 Date: Mon, 29 Aug 2016 06:16:57 -0700 Subject: [PATCH 04/29] feat(influxdb) add elapsed(), fixes #5827 --- public/app/plugins/datasource/influxdb/query_part.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/public/app/plugins/datasource/influxdb/query_part.ts b/public/app/plugins/datasource/influxdb/query_part.ts index 0081481437d..c0592204bc8 100644 --- a/public/app/plugins/datasource/influxdb/query_part.ts +++ b/public/app/plugins/datasource/influxdb/query_part.ts @@ -254,6 +254,15 @@ register({ renderer: functionRenderer, }); +register({ + type: 'elapsed', + addStrategy: addTransformationStrategy, + category: categories.Transformations, + params: [{ name: "duration", type: "interval", options: ['1s', '10s', '1m', '5m', '10m', '15m', '1h']}], + defaultParams: ['10s'], + renderer: functionRenderer, +}); + // Selectors register({ type: 'bottom', From 9c92d8c4e98f1f288a79ddb77cbd7cd34612ea51 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 29 Aug 2016 16:10:19 +0200 Subject: [PATCH 05/29] fix(circle-ci): change to ubuntu 14 for CI --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index ef11d6b038e..5640980b702 100644 --- a/circle.yml +++ b/circle.yml @@ -31,4 +31,4 @@ deployment: branch: master owner: grafana commands: - - ./trigger_grafana_packer.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} + - ./trigger_grafana_packer.sh ${TRIGGER_GRAFANA_PACKER_CIRCLECI_TOKEN} \ No newline at end of file From ff9b95f4b30ceac16b733712227f4c1b407004b8 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 29 Aug 2016 16:24:13 +0200 Subject: [PATCH 06/29] docs(changelog): adds note about closing #5827 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0352a2a3f8f..b9cf39f89a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * **Navigation**: Add search to org swithcer, closes [#2609](https://github.com/grafana/grafana/issues/2609) * **Database**: Allow database config using one propertie, closes [#5456](https://github.com/grafana/grafana/pull/5456) * **Graphite**: Add support for groupByNode, closes [#5613](https://github.com/grafana/grafana/pull/5613) +* **Influxdb**: Add support for elapsed(), closes [#5827](https://github.com/grafana/grafana/pull/5827) # 3.1.2 (unreleased) * **Templating**: Fixed issue when combining row & panel repeats, fixes [#5790](https://github.com/grafana/grafana/issues/5790) From a2525f77d9bd0c01eabc1b8a6e303087a28d2b79 Mon Sep 17 00:00:00 2001 From: bergquist Date: Mon, 29 Aug 2016 21:14:47 +0200 Subject: [PATCH 07/29] fix(config): comment sample value --- conf/sample.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/sample.ini b/conf/sample.ini index 4fcbe5d1157..949f64df025 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -318,7 +318,7 @@ check_for_updates = true # \______(_______;;;)__;;;) [alerting] -enabled = false +;enabled = false #################################### Internal Grafana Metrics ########################## # Metrics available at HTTP API Url /api/metrics From 1f1d232ea67525e75e8e0f1567737a43ec4c4253 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 30 Aug 2016 07:48:15 +0200 Subject: [PATCH 08/29] tech(githooks): remove symlink bash script --- symlink_git_hooks.sh | 5 ----- 1 file changed, 5 deletions(-) delete mode 100755 symlink_git_hooks.sh diff --git a/symlink_git_hooks.sh b/symlink_git_hooks.sh deleted file mode 100755 index 9e272f82ed6..00000000000 --- a/symlink_git_hooks.sh +++ /dev/null @@ -1,5 +0,0 @@ -#/bin/bash - -#ln -s -f .hooks/* .git/hooks/ -cd .git/hooks/ -cp --symbolic-link -f ../../.hooks/* . From 650a87dc05b4a69da8b42b7c79d820e206e40197 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 30 Aug 2016 09:32:56 +0200 Subject: [PATCH 09/29] feat(alerting): add alert history api endpoint ref #5850 --- pkg/api/alerting.go | 32 +++++++++++++++++++++ pkg/api/api.go | 3 +- pkg/api/dtos/alerting.go | 11 ++++++++ pkg/services/annotations/annotations.go | 9 ++++++ pkg/services/sqlstore/annotation.go | 37 +++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 1 deletion(-) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index f20e08a5cad..63a080a74d2 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -8,6 +8,7 @@ import ( "github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/alerting" + "github.com/grafana/grafana/pkg/services/annotations" ) func ValidateOrgAlert(c *middleware.Context) { @@ -212,3 +213,34 @@ func DeleteAlertNotification(c *middleware.Context) Response { return ApiSuccess("Notification deleted") } + +func GetAlertHistory(c *middleware.Context) Response { + query := &annotations.ItemQuery{ + AlertId: c.ParamsInt64("alertId"), + Type: annotations.AlertType, + OrgId: c.OrgId, + Limit: c.QueryInt64("limit"), + } + + repo := annotations.GetRepository() + + items, err := repo.Find(query) + if err != nil { + return ApiError(500, "Failed to get history for alert", err) + } + + var result []dtos.AlertHistory + for _, item := range items { + result = append(result, dtos.AlertHistory{ + AlertId: item.AlertId, + Timestamp: item.Timestamp, + Data: item.Data, + NewState: item.NewState, + Text: item.Text, + Metric: item.Metric, + Title: item.Title, + }) + } + + return Json(200, result) +} diff --git a/pkg/api/api.go b/pkg/api/api.go index 25158586822..966f950ee53 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -250,11 +250,12 @@ func Register(r *macaron.Macaron) { r.Group("/alerts", func() { r.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest)) - //r.Get("/:alertId/states", wrap(GetAlertStates)) r.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert)) r.Get("/", wrap(GetAlerts)) }) + r.Get("/alert-history/:alertId", ValidateOrgAlert, wrap(GetAlertHistory)) + r.Get("/alert-notifications", wrap(GetAlertNotifications)) r.Group("/alert-notifications", func() { diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index 0ec0174a949..c6cafddead0 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -52,3 +52,14 @@ type EvalMatch struct { Metric string `json:"metric"` Value float64 `json:"value"` } + +type AlertHistory struct { + AlertId int64 `json:"alertId"` + NewState string `json:"netState"` + Timestamp time.Time `json:"timestamp"` + Title string `json:"title"` + Text string `json:"text"` + Metric string `json:"metric"` + + Data *simplejson.Json `json:"data"` +} diff --git a/pkg/services/annotations/annotations.go b/pkg/services/annotations/annotations.go index fd8c8ebdb9a..06be152ec31 100644 --- a/pkg/services/annotations/annotations.go +++ b/pkg/services/annotations/annotations.go @@ -8,6 +8,15 @@ import ( type Repository interface { Save(item *Item) error + Find(query *ItemQuery) ([]*Item, error) +} + +type ItemQuery struct { + OrgId int64 `json:"orgId"` + Type ItemType `json:"type"` + AlertId int64 `json:"alertId"` + + Limit int64 `json:"alertId"` } var repositoryInstance Repository diff --git a/pkg/services/sqlstore/annotation.go b/pkg/services/sqlstore/annotation.go index 0530952144e..5cf4461b370 100644 --- a/pkg/services/sqlstore/annotation.go +++ b/pkg/services/sqlstore/annotation.go @@ -1,6 +1,9 @@ package sqlstore import ( + "bytes" + "fmt" + "github.com/go-xorm/xorm" "github.com/grafana/grafana/pkg/services/annotations" ) @@ -17,5 +20,39 @@ func (r *SqlAnnotationRepo) Save(item *annotations.Item) error { return nil }) +} +func (r *SqlAnnotationRepo) Find(query *annotations.ItemQuery) ([]*annotations.Item, error) { + var sql bytes.Buffer + params := make([]interface{}, 0) + + sql.WriteString(`SELECT * + from annotation + `) + + sql.WriteString(`WHERE org_id = ?`) + params = append(params, query.OrgId) + + if query.AlertId != 0 { + sql.WriteString(` AND alert_id = ?`) + params = append(params, query.AlertId) + } + + if query.Type != "" { + sql.WriteString(` AND type = ?`) + params = append(params, string(query.Type)) + } + + if query.Limit == 0 { + query.Limit = 10 + } + + sql.WriteString(fmt.Sprintf("ORDER BY timestamp DESC LIMIT %v", query.Limit)) + + items := make([]*annotations.Item, 0) + if err := x.Sql(sql.String(), params...).Find(&items); err != nil { + return nil, err + } + + return items, nil } From 11a4ff0f8a635fc4ed8a140f30a889d1dd601605 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 30 Aug 2016 13:22:59 +0200 Subject: [PATCH 10/29] feat(alerting): add basic UI for history in alert tab ref #5850 --- pkg/api/alerting.go | 38 ++++++++++++++++++- pkg/api/api.go | 2 +- pkg/api/dtos/alerting.go | 2 +- pkg/services/alerting/result_handler.go | 2 + .../app/features/alerting/alert_tab_ctrl.ts | 16 ++++++++ .../features/alerting/partials/alert_tab.html | 22 +++++++++++ public/sass/components/edit_sidemenu.scss | 2 +- 7 files changed, 80 insertions(+), 4 deletions(-) diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 63a080a74d2..80b5108bbad 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -215,8 +215,13 @@ func DeleteAlertNotification(c *middleware.Context) Response { } func GetAlertHistory(c *middleware.Context) Response { + alertId, err := getAlertIdForRequest(c) + if err != nil { + return ApiError(400, "Invalid request", err) + } + query := &annotations.ItemQuery{ - AlertId: c.ParamsInt64("alertId"), + AlertId: alertId, Type: annotations.AlertType, OrgId: c.OrgId, Limit: c.QueryInt64("limit"), @@ -244,3 +249,34 @@ func GetAlertHistory(c *middleware.Context) Response { return Json(200, result) } + +func getAlertIdForRequest(c *middleware.Context) (int64, error) { + alertId := c.QueryInt64("alertId") + panelId := c.QueryInt64("panelId") + dashboardId := c.QueryInt64("dashboardId") + + if alertId == 0 && dashboardId == 0 && panelId == 0 { + return 0, fmt.Errorf("Missing alertId or dashboardId and panelId") + } + + if alertId == 0 { + //fetch alertId + query := models.GetAlertsQuery{ + OrgId: c.OrgId, + DashboardId: dashboardId, + PanelId: panelId, + } + + if err := bus.Dispatch(&query); err != nil { + return 0, err + } + + if len(query.Result) != 1 { + return 0, fmt.Errorf("PanelId is not unique on dashboard") + } + + alertId = query.Result[0].Id + } + + return alertId, nil +} diff --git a/pkg/api/api.go b/pkg/api/api.go index 966f950ee53..132c609dde4 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -254,7 +254,7 @@ func Register(r *macaron.Macaron) { r.Get("/", wrap(GetAlerts)) }) - r.Get("/alert-history/:alertId", ValidateOrgAlert, wrap(GetAlertHistory)) + r.Get("/alert-history", wrap(GetAlertHistory)) r.Get("/alert-notifications", wrap(GetAlertNotifications)) diff --git a/pkg/api/dtos/alerting.go b/pkg/api/dtos/alerting.go index c6cafddead0..d5fbb2c3a9f 100644 --- a/pkg/api/dtos/alerting.go +++ b/pkg/api/dtos/alerting.go @@ -55,7 +55,7 @@ type EvalMatch struct { type AlertHistory struct { AlertId int64 `json:"alertId"` - NewState string `json:"netState"` + NewState string `json:"newState"` Timestamp time.Time `json:"timestamp"` Title string `json:"title"` Text string `json:"text"` diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index 09c77c8edd7..eca10f87494 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -4,6 +4,7 @@ import ( "time" "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" @@ -65,6 +66,7 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { NewState: string(ctx.Rule.State), PrevState: string(oldState), Timestamp: time.Now(), + Data: simplejson.NewFromAny(ctx.EvalMatches), } annotationRepo := annotations.GetRepository() diff --git a/public/app/features/alerting/alert_tab_ctrl.ts b/public/app/features/alerting/alert_tab_ctrl.ts index 2f222802438..0c42c2dbe7e 100644 --- a/public/app/features/alerting/alert_tab_ctrl.ts +++ b/public/app/features/alerting/alert_tab_ctrl.ts @@ -5,6 +5,7 @@ import {ThresholdMapper} from './threshold_mapper'; import {QueryPart} from 'app/core/components/query_part/query_part'; import alertDef from './alert_def'; import config from 'app/core/config'; +import moment from 'moment'; export class AlertTabCtrl { panel: any; @@ -22,6 +23,7 @@ export class AlertTabCtrl { alertNotifications; error: string; appSubUrl: string; + alertHistory: any; /** @ngInject */ constructor(private $scope, @@ -60,6 +62,7 @@ export class AlertTabCtrl { // build notification model this.notifications = []; this.alertNotifications = []; + this.alertHistory = []; return this.backendSrv.get('/api/alert-notifications').then(res => { this.notifications = res; @@ -71,6 +74,19 @@ export class AlertTabCtrl { this.alertNotifications.push(model); } }); + }).then(() => { + this.backendSrv.get(`/api/alert-history?dashboardId=${this.panelCtrl.dashboard.id}&panelId=${this.panel.id}`).then(res => { + this.alertHistory = _.map(res, (ah) => { + ah.time = moment(ah.timestamp).format('MMM D, YYYY HH:mm:ss'); + ah.stateModel = alertDef.getStateDisplayModel(ah.newState); + + ah.metrics = _.map(ah.data, (ev) => { + return ev.Metric + "=" + ev.Value; + }).join(', '); + + return ah; + }); + }); }); } diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index a386d23945b..7a98cf38a31 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -122,6 +122,28 @@ + +
+
Alert history
+
+
    +
  1. +
    +
    +
    +
    + + + {{ah.text}} + at {{ah.time}} {{ah.metrics}} +
    +
    +
    +
    +
  2. +
+
+
diff --git a/public/sass/components/edit_sidemenu.scss b/public/sass/components/edit_sidemenu.scss index de3e92ee5e2..d6c56fcc521 100644 --- a/public/sass/components/edit_sidemenu.scss +++ b/public/sass/components/edit_sidemenu.scss @@ -5,7 +5,7 @@ } .edit-sidemenu-aside { - width: 14rem; + width: 16rem; } .edit-sidemenu { From b55b7cde4bd13546e8b4d430de28d9529789f37b Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 30 Aug 2016 13:36:13 +0200 Subject: [PATCH 11/29] fix(alerting): remove error message when model is valid again closes #5921 --- public/app/features/alerting/alert_tab_ctrl.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/app/features/alerting/alert_tab_ctrl.ts b/public/app/features/alerting/alert_tab_ctrl.ts index 0c42c2dbe7e..546aee776a7 100644 --- a/public/app/features/alerting/alert_tab_ctrl.ts +++ b/public/app/features/alerting/alert_tab_ctrl.ts @@ -208,6 +208,8 @@ export class AlertTabCtrl { this.error = 'Currently the alerting backend only supports Graphite queries'; } else if (this.templateSrv.variableExists(foundTarget.target)) { this.error = 'Template variables are not supported in alert queries'; + } else { + this.error = ''; } }); } From 645293e590429bdfd4f91acebb575cc064f79c60 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 30 Aug 2016 13:47:42 +0200 Subject: [PATCH 12/29] fix(cli): improve error message for upgrade-all closes #5885 --- pkg/cmd/grafana-cli/commands/commands.go | 2 +- pkg/cmd/grafana-cli/commands/upgrade_all_command.go | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/grafana-cli/commands/commands.go b/pkg/cmd/grafana-cli/commands/commands.go index 2099a576647..b301924d956 100644 --- a/pkg/cmd/grafana-cli/commands/commands.go +++ b/pkg/cmd/grafana-cli/commands/commands.go @@ -14,7 +14,7 @@ func runCommand(command func(commandLine CommandLine) error) func(context *cli.C cmd := &contextCommandLine{context} if err := command(cmd); err != nil { logger.Errorf("\n%s: ", color.RedString("Error")) - logger.Errorf("%s\n\n", err) + logger.Errorf("%s %s\n\n", color.RedString("✗"), err) cmd.ShowHelp() os.Exit(1) diff --git a/pkg/cmd/grafana-cli/commands/upgrade_all_command.go b/pkg/cmd/grafana-cli/commands/upgrade_all_command.go index 1a6df719053..636292cce11 100644 --- a/pkg/cmd/grafana-cli/commands/upgrade_all_command.go +++ b/pkg/cmd/grafana-cli/commands/upgrade_all_command.go @@ -53,8 +53,16 @@ func upgradeAllCommand(c CommandLine) error { for _, p := range pluginsToUpgrade { logger.Infof("Updating %v \n", p.Id) - s.RemoveInstalledPlugin(pluginsDir, p.Id) - InstallPlugin(p.Id, "", c) + var err error + err = s.RemoveInstalledPlugin(pluginsDir, p.Id) + if err != nil { + return err + } + + err = InstallPlugin(p.Id, "", c) + if err != nil { + return err + } } return nil From ae428b917796a06da517c7c94465e65012d3824a Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 30 Aug 2016 13:49:50 +0200 Subject: [PATCH 13/29] docs(changelog): add note about cli fix --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9cf39f89a9..c4614d024d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * **Drag&Drop**: Fixed issue with drag and drop in latest Chrome(51+), fixes [#5767](https://github.com/grafana/grafana/issues/5767) * **Internal Metrics**: Fixed issue with dots in instance_name when sending internal metrics to Graphite, fixes [#5739](https://github.com/grafana/grafana/issues/5739) * **Grafana-CLI**: Add default plugin path for MAC OS, fixes [#5806](https://github.com/grafana/grafana/issues/5806) +* **Grafana-CLI**: Improve error message for upgrade-all command, fixes [#5885](https://github.com/grafana/grafana/issues/5885) # 3.1.1 (2016-08-01) * **IFrame embedding**: Fixed issue of using full iframe height, fixes [#5605](https://github.com/grafana/grafana/issues/5606) From a5d49c98202f72696bcc64a17c333f12138ed0c9 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 31 Aug 2016 08:33:58 +0200 Subject: [PATCH 14/29] docs(config): add note about poodle vulnerabillity in <3.0 closes #3483 --- docs/sources/installation/configuration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index af3bb796e2d..4bb4d262d46 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -88,6 +88,8 @@ Another way is put a webserver like Nginx or Apache in front of Grafana and have `http` or `https` +> **Note** Grafana versions earlier than 3.0 are vulnerable to [POODLE](https://en.wikipedia.org/wiki/POODLE). So we strongly recommend to upgrade to 3.x or use a reverse proxy for ssl termination. + ### domain This setting is only used in as a part of the `root_url` setting (see below). Important if you From 53a9dec407b86eb3b2e22803bf97513cefd9581a Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 31 Aug 2016 09:48:58 +0200 Subject: [PATCH 15/29] feat(alerting): move timestamp to new row ref #5850 --- public/app/features/alerting/alert_tab_ctrl.ts | 4 ++-- public/app/features/alerting/partials/alert_tab.html | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/public/app/features/alerting/alert_tab_ctrl.ts b/public/app/features/alerting/alert_tab_ctrl.ts index 546aee776a7..c5ed6187f35 100644 --- a/public/app/features/alerting/alert_tab_ctrl.ts +++ b/public/app/features/alerting/alert_tab_ctrl.ts @@ -76,11 +76,11 @@ export class AlertTabCtrl { }); }).then(() => { this.backendSrv.get(`/api/alert-history?dashboardId=${this.panelCtrl.dashboard.id}&panelId=${this.panel.id}`).then(res => { - this.alertHistory = _.map(res, (ah) => { + this.alertHistory = _.map(res, ah => { ah.time = moment(ah.timestamp).format('MMM D, YYYY HH:mm:ss'); ah.stateModel = alertDef.getStateDisplayModel(ah.newState); - ah.metrics = _.map(ah.data, (ev) => { + ah.metrics = _.map(ah.data, ev=> { return ev.Metric + "=" + ev.Value; }).join(', '); diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index 7a98cf38a31..497027203bc 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -134,8 +134,11 @@
- {{ah.text}} - at {{ah.time}} {{ah.metrics}} + {{ah.stateModel.text}} + {{ah.metrics}} +
+
+ {{ah.time}}
From e493015f54137c5cb1d0ae5cf356d52244d25d1b Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 31 Aug 2016 10:10:39 +0200 Subject: [PATCH 16/29] fix(alerting): replace one hack with another hack --- public/app/features/alerting/partials/alert_tab.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/alerting/partials/alert_tab.html b/public/app/features/alerting/partials/alert_tab.html index 497027203bc..e755496b732 100644 --- a/public/app/features/alerting/partials/alert_tab.html +++ b/public/app/features/alerting/partials/alert_tab.html @@ -123,7 +123,7 @@ -
+
Alert history
    From c624f3d47048a538a27bddf3144a70c3bb6872af Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 31 Aug 2016 11:55:35 +0200 Subject: [PATCH 17/29] fix(alerting): measure state result instead of severity --- pkg/metrics/metrics.go | 69 +++++++++++++------------ pkg/services/alerting/result_handler.go | 24 +++++---- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 9477f2738b5..80c7520eaf2 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -9,34 +9,36 @@ func init() { } var ( - M_Instance_Start Counter - M_Page_Status_200 Counter - M_Page_Status_500 Counter - M_Page_Status_404 Counter - M_Api_Status_500 Counter - M_Api_Status_404 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_Critical Counter - M_Alerting_Result_Warning Counter - M_Alerting_Result_Info Counter - M_Alerting_Result_Ok 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_Api_Status_500 Counter + M_Api_Status_404 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_Critical Counter + M_Alerting_Result_State_Warning Counter + M_Alerting_Result_State_Ok Counter + M_Alerting_Result_State_Paused Counter + M_Alerting_Result_State_Pending Counter + M_Alerting_Result_State_ExecutionError Counter + M_Alerting_Active_Alerts Counter + M_Alerting_Notification_Sent_Slack Counter + M_Alerting_Notification_Sent_Email Counter + M_Alerting_Notification_Sent_Webhook Counter // Timers M_DataSource_ProxyReq_Timer Timer @@ -75,10 +77,13 @@ func initMetricVars(settings *MetricSettings) { M_Models_Dashboard_Insert = RegCounter("models.dashboard.insert") - M_Alerting_Result_Critical = RegCounter("alerting.result", "severity", "critical") - M_Alerting_Result_Warning = RegCounter("alerting.result", "severity", "warning") - M_Alerting_Result_Info = RegCounter("alerting.result", "severity", "info") - M_Alerting_Result_Ok = RegCounter("alerting.result", "severity", "ok") + M_Alerting_Result_State_Critical = RegCounter("alerting.result", "state", "critical") + M_Alerting_Result_State_Warning = RegCounter("alerting.result", "state", "warning") + M_Alerting_Result_State_Ok = RegCounter("alerting.result", "state", "ok") + M_Alerting_Result_State_Paused = RegCounter("alerting.result", "state", "paused") + M_Alerting_Result_State_Pending = RegCounter("alerting.result", "state", "pending") + M_Alerting_Result_State_ExecutionError = RegCounter("alerting.result", "state", "execution_error") + M_Alerting_Active_Alerts = RegCounter("alerting.active_alerts") M_Alerting_Notification_Sent_Slack = RegCounter("alerting.notifications_sent", "type", "slack") M_Alerting_Notification_Sent_Email = RegCounter("alerting.notifications_sent", "type", "email") diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index eca10f87494..6f19c0e7e52 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -41,7 +41,7 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { ctx.Rule.State = m.AlertStateOK } - countSeverity(ctx.Rule.Severity) + countStateResult(ctx.Rule.State) if ctx.Rule.State != oldState { handler.log.Info("New state change", "alertId", ctx.Rule.Id, "newState", ctx.Rule.State, "oldState", oldState) @@ -78,15 +78,19 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { } } -func countSeverity(state m.AlertSeverityType) { +func countStateResult(state m.AlertStateType) { switch state { - case m.AlertSeverityOK: - metrics.M_Alerting_Result_Ok.Inc(1) - case m.AlertSeverityInfo: - metrics.M_Alerting_Result_Info.Inc(1) - case m.AlertSeverityWarning: - metrics.M_Alerting_Result_Warning.Inc(1) - case m.AlertSeverityCritical: - metrics.M_Alerting_Result_Critical.Inc(1) + case m.AlertStateCritical: + metrics.M_Alerting_Result_State_Critical.Inc(1) + case m.AlertStateWarning: + metrics.M_Alerting_Result_State_Warning.Inc(1) + case m.AlertStateOK: + metrics.M_Alerting_Result_State_Ok.Inc(1) + case m.AlertStatePaused: + metrics.M_Alerting_Result_State_Paused.Inc(1) + case m.AlertStatePending: + metrics.M_Alerting_Result_State_Pending.Inc(1) + case m.AlertStateExeuctionError: + metrics.M_Alerting_Result_State_ExecutionError.Inc(1) } } From 4619a05f43a12b8592a46f10db8ff3f37c28f9f5 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 31 Aug 2016 14:06:54 +0200 Subject: [PATCH 18/29] feat(alerting): save execution error message to annotations --- pkg/services/alerting/result_handler.go | 5 ++++- pkg/tsdb/graphite/graphite.go | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index 6f19c0e7e52..f6c4e90138f 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -31,12 +31,15 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { oldState := ctx.Rule.State exeuctionError := "" + annotationData := simplejson.New() if ctx.Error != nil { handler.log.Error("Alert Rule Result Error", "ruleId", ctx.Rule.Id, "error", ctx.Error) ctx.Rule.State = m.AlertStateExeuctionError exeuctionError = ctx.Error.Error() + annotationData.Set("errorMessage", exeuctionError) } else if ctx.Firing { ctx.Rule.State = m.AlertStateType(ctx.Rule.Severity) + annotationData = simplejson.NewFromAny(ctx.EvalMatches) } else { ctx.Rule.State = m.AlertStateOK } @@ -66,7 +69,7 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { NewState: string(ctx.Rule.State), PrevState: string(oldState), Timestamp: time.Now(), - Data: simplejson.NewFromAny(ctx.EvalMatches), + Data: annotationData, } annotationRepo := annotations.GetRepository() diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 68f92d7527e..874f2b7959b 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -49,12 +49,12 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC } res, err := client.Do(req) - defer res.Body.Close() if err != nil { result.Error = err return result } + defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { result.Error = err From 7ddd625e9dd253b33654104771f0e3119d9fdbb7 Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 31 Aug 2016 15:55:01 +0200 Subject: [PATCH 19/29] feat(alerting): add slack/email support for execution errors --- pkg/services/alerting/eval_context.go | 60 ++++++++++++++---------- pkg/services/alerting/notifier.go | 1 - pkg/services/alerting/notifiers/email.go | 2 +- pkg/services/alerting/notifiers/slack.go | 17 ++++++- pkg/services/alerting/result_handler.go | 2 +- 5 files changed, 52 insertions(+), 30 deletions(-) diff --git a/pkg/services/alerting/eval_context.go b/pkg/services/alerting/eval_context.go index 70dc6b17a2f..29c51e02abd 100644 --- a/pkg/services/alerting/eval_context.go +++ b/pkg/services/alerting/eval_context.go @@ -28,36 +28,46 @@ type EvalContext struct { ImageOnDiskPath string } +type StateDescription struct { + Color string + Text string + Data string +} + +func (c *EvalContext) GetStateModel() *StateDescription { + if c.Error != nil { + return &StateDescription{ + Color: "#D63232", + Text: "EXECUTION ERROR", + } + } + + if !c.Firing { + return &StateDescription{ + Color: "#36a64f", + Text: "OK", + } + } + + if c.Rule.Severity == m.AlertSeverityWarning { + return &StateDescription{ + Color: "#fd821b", + Text: "WARNING", + } + } else { + return &StateDescription{ + Color: "#D63232", + Text: "CRITICAL", + } + } +} + func (a *EvalContext) GetDurationMs() float64 { return float64(a.EndTime.Nanosecond()-a.StartTime.Nanosecond()) / float64(1000000) } -func (c *EvalContext) GetColor() string { - if !c.Firing { - return "#36a64f" - } - - if c.Rule.Severity == m.AlertSeverityWarning { - return "#fd821b" - } else { - return "#D63232" - } -} - -func (c *EvalContext) GetStateText() string { - if !c.Firing { - return "OK" - } - - if c.Rule.Severity == m.AlertSeverityWarning { - return "WARNING" - } else { - return "CRITICAL" - } -} - func (c *EvalContext) GetNotificationTitle() string { - return "[" + c.GetStateText() + "] " + c.Rule.Name + return "[" + c.GetStateModel().Text + "] " + c.Rule.Name } func (c *EvalContext) getDashboardSlug() (string, error) { diff --git a/pkg/services/alerting/notifier.go b/pkg/services/alerting/notifier.go index 826aede2d11..c345594de2c 100644 --- a/pkg/services/alerting/notifier.go +++ b/pkg/services/alerting/notifier.go @@ -48,7 +48,6 @@ func (n *RootNotifier) Notify(context *EvalContext) { for _, notifier := range notifiers { n.log.Info("Sending notification", "firing", context.Firing, "type", notifier.GetType()) - go notifier.Notify(context) } } diff --git a/pkg/services/alerting/notifiers/email.go b/pkg/services/alerting/notifiers/email.go index 20b0bc06463..d63ca7d13f5 100644 --- a/pkg/services/alerting/notifiers/email.go +++ b/pkg/services/alerting/notifiers/email.go @@ -54,7 +54,7 @@ func (this *EmailNotifier) Notify(context *alerting.EvalContext) { "State": context.Rule.State, "Name": context.Rule.Name, "Severity": context.Rule.Severity, - "SeverityColor": context.GetColor(), + "SeverityColor": context.GetStateModel().Color, "Message": context.Rule.Message, "RuleUrl": ruleUrl, "ImageLink": context.ImagePublicUrl, diff --git a/pkg/services/alerting/notifiers/slack.go b/pkg/services/alerting/notifiers/slack.go index e099c7da690..837621e80bb 100644 --- a/pkg/services/alerting/notifiers/slack.go +++ b/pkg/services/alerting/notifiers/slack.go @@ -61,13 +61,26 @@ func (this *SlackNotifier) Notify(context *alerting.EvalContext) { } } + if context.Error != nil { + fields = append(fields, map[string]interface{}{ + "title": "Error message", + "value": context.Error.Error(), + "short": false, + }) + } + + message := "" + if context.Rule.State != m.AlertStateOK { //dont add message when going back to alert state ok. + message = context.Rule.Message + } + body := map[string]interface{}{ "attachments": []map[string]interface{}{ { - "color": context.GetColor(), + "color": context.GetStateModel().Color, "title": context.GetNotificationTitle(), "title_link": ruleUrl, - "text": context.Rule.Message, + "text": message, "fields": fields, "image_url": context.ImagePublicUrl, "footer": "Grafana v" + setting.BuildVersion, diff --git a/pkg/services/alerting/result_handler.go b/pkg/services/alerting/result_handler.go index f6c4e90138f..f22afb279d8 100644 --- a/pkg/services/alerting/result_handler.go +++ b/pkg/services/alerting/result_handler.go @@ -65,7 +65,7 @@ func (handler *DefaultResultHandler) Handle(ctx *EvalContext) { Type: annotations.AlertType, AlertId: ctx.Rule.Id, Title: ctx.Rule.Name, - Text: ctx.GetStateText(), + Text: ctx.GetStateModel().Text, NewState: string(ctx.Rule.State), PrevState: string(oldState), Timestamp: time.Now(), From 000eb669dfcb0969b75e309f820fe186ef78c624 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 1 Sep 2016 08:13:48 +0200 Subject: [PATCH 20/29] tech(build): change min go version to 1.6 --- README.md | 2 +- build.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6dbfc5388c2..f8d1db6cd07 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ the latest master builds [here](http://grafana.org/download/builds) ### Dependencies -- Go 1.5 +- Go 1.6 - NodeJS v4+ - [Godep](https://github.com/tools/godep) diff --git a/build.go b/build.go index 4347c486063..9b8aae6a68a 100644 --- a/build.go +++ b/build.go @@ -34,7 +34,7 @@ var ( binaries []string = []string{"grafana-server", "grafana-cli"} ) -const minGoVersion = 1.3 +const minGoVersion = 1.6 func main() { log.SetOutput(os.Stdout) From 29b60329cc5763f9c7341a574f15c27e15ebc278 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 1 Sep 2016 09:17:57 +0200 Subject: [PATCH 21/29] stupid stupid stupid me --- pkg/tsdb/graphite/graphite.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 874f2b7959b..267f360f797 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -45,7 +45,7 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC client := http.Client{Timeout: time.Duration(10 * time.Second)} req, _ := http.NewRequest(http.MethodPost, e.Url+"/render?", strings.NewReader(params.Encode())) if e.BasicAuth { - req.SetBasicAuth("carl", "carl") + req.SetBasicAuth(e.BasicAuthPassword, e.BasicAuthPassword) } res, err := client.Do(req) From 94389a3a4460bd291a9ff86051ec04752c6f9863 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 1 Sep 2016 09:38:43 +0200 Subject: [PATCH 22/29] tech(tsdb): improve logging for graphite client --- pkg/tsdb/graphite/graphite.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 267f360f797..21db69ec488 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -2,6 +2,7 @@ package graphite import ( "encoding/json" + "fmt" "io/ioutil" "net/http" "net/url" @@ -45,7 +46,7 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC client := http.Client{Timeout: time.Duration(10 * time.Second)} req, _ := http.NewRequest(http.MethodPost, e.Url+"/render?", strings.NewReader(params.Encode())) if e.BasicAuth { - req.SetBasicAuth(e.BasicAuthPassword, e.BasicAuthPassword) + req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword) } res, err := client.Do(req) @@ -61,10 +62,16 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC return result } + if res.StatusCode == http.StatusUnauthorized { + glog.Info("Request is Unauthorized", "status", res.Status, "body", string(body)) + result.Error = fmt.Errorf("Request is Unauthorized status: %v body: %s", res.Status, string(body)) + return result + } + var data []TargetResponseDTO err = json.Unmarshal(body, &data) if err != nil { - glog.Info("Failed to unmarshal graphite response", "error", err, "body", string(body)) + glog.Info("Failed to unmarshal graphite response", "error", err, "status", res.Status, "body", string(body)) result.Error = err return result } From 3d3365f2428773f6852b3e89ea6cfbe70bcbd5dd Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 1 Sep 2016 10:09:13 +0200 Subject: [PATCH 23/29] tech(graphite): add more logging --- pkg/tsdb/graphite/graphite.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 21db69ec488..a6295292d7e 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -13,6 +13,10 @@ import ( "github.com/grafana/grafana/pkg/tsdb" ) +var ( + HttpClient = http.Client{Timeout: time.Duration(10 * time.Second)} +) + type GraphiteExecutor struct { *tsdb.DataSourceInfo } @@ -43,20 +47,27 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC glog.Debug("Graphite request", "query", query.Query) } - client := http.Client{Timeout: time.Duration(10 * time.Second)} - req, _ := http.NewRequest(http.MethodPost, e.Url+"/render?", strings.NewReader(params.Encode())) + formData := params.Encode() + glog.Info("Graphite request body", "formdata", formData) + req, err := http.NewRequest(http.MethodPost, e.Url+"/render", strings.NewReader(formData)) + if err != nil { + glog.Info("Failed to create request", "error", err) + result.Error = fmt.Errorf("Failed to create request. error: %v", err) + return result + } + if e.BasicAuth { req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword) } - res, err := client.Do(req) + res, err := HttpClient.Do(req) if err != nil { result.Error = err return result } - defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() if err != nil { result.Error = err return result From d73547c0dc165e157adea006db7a539fe8e5e903 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 1 Sep 2016 11:12:35 +0200 Subject: [PATCH 24/29] feat(tsdb): add missing content type and join url using path --- pkg/tsdb/graphite/graphite.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index a6295292d7e..793aeefe568 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "net/http" "net/url" + "path" "strings" "time" @@ -44,18 +45,20 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC for _, query := range queries { params["target"] = []string{query.Query} - glog.Debug("Graphite request", "query", query.Query) } - formData := params.Encode() - glog.Info("Graphite request body", "formdata", formData) - req, err := http.NewRequest(http.MethodPost, e.Url+"/render", strings.NewReader(formData)) + u, _ := url.Parse(e.Url) + u.Path = path.Join(u.Path, "render") + glog.Info("Graphite request body", "formdata", params.Encode()) + + req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(params.Encode())) if err != nil { glog.Info("Failed to create request", "error", err) result.Error = fmt.Errorf("Failed to create request. error: %v", err) return result } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") if e.BasicAuth { req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword) } From 86aea8921498f1aec38defbc8bedd10957060642 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 1 Sep 2016 13:03:15 +0200 Subject: [PATCH 25/29] style(tsdb): extract some methods --- pkg/tsdb/graphite/graphite.go | 76 +++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/pkg/tsdb/graphite/graphite.go b/pkg/tsdb/graphite/graphite.go index 793aeefe568..0247ad0dca8 100644 --- a/pkg/tsdb/graphite/graphite.go +++ b/pkg/tsdb/graphite/graphite.go @@ -36,7 +36,7 @@ func init() { func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryContext) *tsdb.BatchResult { result := &tsdb.BatchResult{} - params := url.Values{ + formData := url.Values{ "from": []string{"-" + formatTimeRange(context.TimeRange.From)}, "until": []string{formatTimeRange(context.TimeRange.To)}, "format": []string{"json"}, @@ -44,52 +44,28 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC } for _, query := range queries { - params["target"] = []string{query.Query} + formData["target"] = []string{query.Query} } - u, _ := url.Parse(e.Url) - u.Path = path.Join(u.Path, "render") - glog.Info("Graphite request body", "formdata", params.Encode()) + glog.Info("Graphite request body", "formdata", formData.Encode()) - req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(params.Encode())) + req, err := e.createRequest(formData) if err != nil { - glog.Info("Failed to create request", "error", err) - result.Error = fmt.Errorf("Failed to create request. error: %v", err) + result.Error = err return result } - - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - if e.BasicAuth { - req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword) - } - res, err := HttpClient.Do(req) if err != nil { result.Error = err return result } - body, err := ioutil.ReadAll(res.Body) - defer res.Body.Close() + data, err := e.parseResponse(res) if err != nil { result.Error = err return result } - if res.StatusCode == http.StatusUnauthorized { - glog.Info("Request is Unauthorized", "status", res.Status, "body", string(body)) - result.Error = fmt.Errorf("Request is Unauthorized status: %v body: %s", res.Status, string(body)) - return result - } - - var data []TargetResponseDTO - err = json.Unmarshal(body, &data) - if err != nil { - glog.Info("Failed to unmarshal graphite response", "error", err, "status", res.Status, "body", string(body)) - result.Error = err - return result - } - result.QueryResults = make(map[string]*tsdb.QueryResult) queryRes := &tsdb.QueryResult{} for _, series := range data { @@ -103,6 +79,46 @@ func (e *GraphiteExecutor) Execute(queries tsdb.QuerySlice, context *tsdb.QueryC return result } +func (e *GraphiteExecutor) parseResponse(res *http.Response) ([]TargetResponseDTO, error) { + body, err := ioutil.ReadAll(res.Body) + defer res.Body.Close() + if err != nil { + return nil, err + } + + if res.StatusCode == http.StatusUnauthorized { + glog.Info("Request is Unauthorized", "status", res.Status, "body", string(body)) + return nil, fmt.Errorf("Request is Unauthorized status: %v body: %s", res.Status, string(body)) + } + + var data []TargetResponseDTO + err = json.Unmarshal(body, &data) + if err != nil { + glog.Info("Failed to unmarshal graphite response", "error", err, "status", res.Status, "body", string(body)) + return nil, err + } + + return data, nil +} + +func (e *GraphiteExecutor) createRequest(data url.Values) (*http.Request, error) { + u, _ := url.Parse(e.Url) + u.Path = path.Join(u.Path, "render") + + req, err := http.NewRequest(http.MethodPost, u.String(), strings.NewReader(data.Encode())) + if err != nil { + glog.Info("Failed to create request", "error", err) + return nil, fmt.Errorf("Failed to create request. error: %v", err) + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + if e.BasicAuth { + req.SetBasicAuth(e.BasicAuthUser, e.BasicAuthPassword) + } + + return req, err +} + func formatTimeRange(input string) string { if input == "now" { return input From 1955defc4357c2136240421bb78536870469ad4a Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 1 Sep 2016 17:21:13 +0200 Subject: [PATCH 26/29] fix(api): add missing jsonData field to dto The field is available on the dto so I guess is should be used. Otherwise we should remove the field rather then not setting it. ref #5944 --- pkg/api/datasources.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index 62a83bdaa0b..18b48cd8e29 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -22,7 +22,6 @@ func GetDataSources(c *middleware.Context) { result := make(dtos.DataSourceList, 0) for _, ds := range query.Result { - dsItem := dtos.DataSource{ Id: ds.Id, OrgId: ds.OrgId, @@ -35,6 +34,7 @@ func GetDataSources(c *middleware.Context) { User: ds.User, BasicAuth: ds.BasicAuth, IsDefault: ds.IsDefault, + JsonData: ds.JsonData, } if plugin, exists := plugins.DataSources[ds.Type]; exists { From 3645fb371c1118d4938ddb1c55b4a415fec0e7e9 Mon Sep 17 00:00:00 2001 From: Hiroaki Kobayashi Date: Fri, 2 Sep 2016 15:22:04 +0900 Subject: [PATCH 27/29] fix typo --- public/app/features/dashboard/import/dash_import.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/features/dashboard/import/dash_import.html b/public/app/features/dashboard/import/dash_import.html index 64a4a91a3c8..245e147dd3d 100644 --- a/public/app/features/dashboard/import/dash_import.html +++ b/public/app/features/dashboard/import/dash_import.html @@ -36,7 +36,7 @@
    - +