From a747ec349e9422656476bae805a56b1aa1488bc6 Mon Sep 17 00:00:00 2001 From: bergquist Date: Thu, 8 Mar 2018 09:54:26 +0100 Subject: [PATCH 1/9] release 5.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8197a695e72..68e9db01749 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Grafana Labs" }, "name": "grafana", - "version": "5.0.0", + "version": "5.0.1", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" From e1b554c61eb3e83ec3faae7bb4e79e709608de80 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 13 Mar 2018 21:06:25 +0100 Subject: [PATCH 2/9] dashboards: should be possible to browse dashboard using only uid That is, the slug part of url should be optional. Closes #11231 (cherry picked from commit 5fbfd67b945536f3d34577034254da9a7f805115) --- pkg/api/api.go | 1 + public/app/routes/routes.ts | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/pkg/api/api.go b/pkg/api/api.go index 1b7e30e34ad..d8def3453c4 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -66,6 +66,7 @@ func (hs *HttpServer) registerRoutes() { r.Get("/plugins/:id/page/:page", reqSignedIn, Index) r.Get("/d/:uid/:slug", reqSignedIn, Index) + r.Get("/d/:uid", reqSignedIn, Index) r.Get("/dashboard/db/:slug", reqSignedIn, redirectFromLegacyDashboardUrl, Index) r.Get("/dashboard/script/*", reqSignedIn, Index) r.Get("/dashboard-solo/snapshot/*", Index) diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index cab45b5aff3..e968bee9be4 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -22,6 +22,12 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { reloadOnSearch: false, pageClass: 'page-dashboard', }) + .when('/d/:uid', { + templateUrl: 'public/app/partials/dashboard.html', + controller: 'LoadDashboardCtrl', + reloadOnSearch: false, + pageClass: 'page-dashboard', + }) .when('/dashboard/:type/:slug', { templateUrl: 'public/app/partials/dashboard.html', controller: 'LoadDashboardCtrl', From 9d58257be61bad82fff840a41fc71a609e79d9fe Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 13 Mar 2018 21:32:37 +0100 Subject: [PATCH 3/9] folders: should be possible to browse folder using only uid That is, the slug part of url should be optional. (cherry picked from commit 91fa076fb335b26c17d2352bb951384dc97f31c4) --- public/app/routes/routes.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index e968bee9be4..487e9b27b22 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -104,6 +104,11 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { controller: 'FolderDashboardsCtrl', controllerAs: 'ctrl', }) + .when('/dashboards/f/:uid', { + templateUrl: 'public/app/features/dashboard/partials/folder_dashboards.html', + controller: 'FolderDashboardsCtrl', + controllerAs: 'ctrl', + }) .when('/org', { templateUrl: 'public/app/features/org/partials/orgDetails.html', controller: 'OrgDetailsCtrl', From 80c717cf6d4dacafa8e32698aa68a7bff0690c51 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 13 Mar 2018 22:23:37 +0100 Subject: [PATCH 4/9] alerting: supports extracting alerts from collapsed panels collapsed rows wrap the hidden rows within itself. This caused the extractor to miss the panel and therefore delete the alert accosiated with the graph. closes #11222 (cherry picked from commit be7ec310b1112196a6584ddf3d1ddcf50ef22979) --- pkg/services/alerting/extractor.go | 15 + pkg/services/alerting/extractor_test.go | 26 + .../alerting/test-data/collapsed-panels.json | 597 ++++++++++++++++++ 3 files changed, 638 insertions(+) create mode 100644 pkg/services/alerting/test-data/collapsed-panels.json diff --git a/pkg/services/alerting/extractor.go b/pkg/services/alerting/extractor.go index 5206c81642e..2ae26c1a382 100644 --- a/pkg/services/alerting/extractor.go +++ b/pkg/services/alerting/extractor.go @@ -74,6 +74,21 @@ func (e *DashAlertExtractor) GetAlertFromPanels(jsonWithPanels *simplejson.Json) for _, panelObj := range jsonWithPanels.Get("panels").MustArray() { panel := simplejson.NewFromAny(panelObj) + + collapsedJson, collapsed := panel.CheckGet("collapsed") + // check if the panel is collapsed + if collapsed && collapsedJson.MustBool() { + + // extract alerts from sub panels for collapsed panels + als, err := e.GetAlertFromPanels(panel) + if err != nil { + return nil, err + } + + alerts = append(alerts, als...) + continue + } + jsonAlert, hasAlert := panel.CheckGet("alert") if !hasAlert { diff --git a/pkg/services/alerting/extractor_test.go b/pkg/services/alerting/extractor_test.go index f8b678e66bd..3bda6c771fb 100644 --- a/pkg/services/alerting/extractor_test.go +++ b/pkg/services/alerting/extractor_test.go @@ -22,6 +22,7 @@ func TestAlertRuleExtraction(t *testing.T) { defaultDs := &m.DataSource{Id: 12, OrgId: 1, Name: "I am default", IsDefault: true} graphite2Ds := &m.DataSource{Id: 15, OrgId: 1, Name: "graphite2"} influxDBDs := &m.DataSource{Id: 16, OrgId: 1, Name: "InfluxDB"} + prom := &m.DataSource{Id: 17, OrgId: 1, Name: "Prometheus"} bus.AddHandler("test", func(query *m.GetDataSourcesQuery) error { query.Result = []*m.DataSource{defaultDs, graphite2Ds} @@ -38,6 +39,10 @@ func TestAlertRuleExtraction(t *testing.T) { if query.Name == influxDBDs.Name { query.Result = influxDBDs } + if query.Name == prom.Name { + query.Result = prom + } + return nil }) @@ -214,5 +219,26 @@ func TestAlertRuleExtraction(t *testing.T) { } }) }) + + Convey("Should be able to extract collapsed panels", func() { + json, err := ioutil.ReadFile("./test-data/collapsed-panels.json") + So(err, ShouldBeNil) + + dashJson, err := simplejson.NewJson(json) + So(err, ShouldBeNil) + + dash := m.NewDashboardFromJson(dashJson) + extractor := NewDashAlertExtractor(dash, 1) + + alerts, err := extractor.GetAlerts() + + Convey("Get rules without error", func() { + So(err, ShouldBeNil) + }) + + Convey("should be able to extract collapsed alerts", func() { + So(len(alerts), ShouldEqual, 4) + }) + }) }) } diff --git a/pkg/services/alerting/test-data/collapsed-panels.json b/pkg/services/alerting/test-data/collapsed-panels.json new file mode 100644 index 00000000000..29109788955 --- /dev/null +++ b/pkg/services/alerting/test-data/collapsed-panels.json @@ -0,0 +1,597 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 127, + "links": [], + "panels": [ + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 9, + "title": "Row title", + "type": "row" + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 200 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "name": "Panel Title alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_goroutines", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 200 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Panel Title", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 14, + "limit": 10, + "links": [], + "onlyAlertsOnDashboard": true, + "show": "current", + "sortOrder": 1, + "stateFilter": [], + "title": "Panel Title", + "type": "alertlist" + }, + { + "collapsed": true, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 10 + }, + "id": 6, + "panels": [ + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 200 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "name": "Panel 2 alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 11 + }, + "id": 11, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_goroutines", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 200 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Panel 2", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 200 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "name": "Panel 4 alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 11 + }, + "id": 15, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_goroutines", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 200 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Panel 4", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "title": "Row title", + "type": "row" + }, + { + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 4, + "title": "Row title", + "type": "row" + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 200 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "frequency": "10s", + "handler": 1, + "name": "Panel 3 alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_goroutines", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{job}}", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 200 + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Panel 3", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ] + } + ], + "schemaVersion": 16, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "New dashboard Copy", + "uid": "6v5pg36zk", + "version": 17 +} From 3600f0ec0bf11e7642246660b35190723fb4828b Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 14 Mar 2018 14:46:03 +0100 Subject: [PATCH 5/9] session: recover from panics in session middleware backport of 5f511deefcc247677e963e37986b076646a2f080i since 5.0.x diverged from master. ref #11155 --- pkg/middleware/session.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pkg/middleware/session.go b/pkg/middleware/session.go index 4de111ff3d2..5389ad36bdf 100644 --- a/pkg/middleware/session.go +++ b/pkg/middleware/session.go @@ -113,6 +113,18 @@ type SessionWrapper struct { } func (s *SessionWrapper) Start(c *Context) error { + // See https://github.com/grafana/grafana/issues/11155 for details on why + // a recover and retry is needed + defer func() error { + if err := recover(); err != nil { + var retryErr error + s.session, retryErr = s.manager.Start(c.Context) + return retryErr + } + + return nil + }() + var err error s.session, err = s.manager.Start(c.Context) return err From 7133cc1013bf32481d7dcabd894158803d91736b Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 14 Mar 2018 14:24:56 +0100 Subject: [PATCH 6/9] dashboard: fix import dashboard with alert rule Importing a dashboard with alert rule(s) should be possible without receiving invalid alert data error. This fix reverts the import logic to how it worked before Grafana v5.0, that is import will allow dashboard with alert rule(s) but no alerts will be created. After an import the user will need to update the dashboard for the alerts to be created. Fixes #11227 (cherry picked from commit 87284d284e850305bce310eb99705de03dc76f2b) --- pkg/plugins/dashboard_importer.go | 2 +- pkg/services/dashboards/dashboard_service.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/plugins/dashboard_importer.go b/pkg/plugins/dashboard_importer.go index 53012f3e817..fb4d63a1fe4 100644 --- a/pkg/plugins/dashboard_importer.go +++ b/pkg/plugins/dashboard_importer.go @@ -80,7 +80,7 @@ func ImportDashboard(cmd *ImportDashboardCommand) error { User: cmd.User, } - savedDash, err := dashboards.NewService().SaveDashboard(dto) + savedDash, err := dashboards.NewService().ImportDashboard(dto) if err != nil { return err diff --git a/pkg/services/dashboards/dashboard_service.go b/pkg/services/dashboards/dashboard_service.go index 1f39394d757..02a6ffc8330 100644 --- a/pkg/services/dashboards/dashboard_service.go +++ b/pkg/services/dashboards/dashboard_service.go @@ -13,6 +13,7 @@ import ( // DashboardService service for operating on dashboards type DashboardService interface { SaveDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) + ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) } // DashboardProvisioningService service for operating on provisioned dashboards @@ -214,6 +215,20 @@ func (dr *dashboardServiceImpl) SaveDashboard(dto *SaveDashboardDTO) (*models.Da return cmd.Result, nil } +func (dr *dashboardServiceImpl) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) { + cmd, err := dr.buildSaveDashboardCommand(dto, false) + if err != nil { + return nil, err + } + + err = bus.Dispatch(cmd) + if err != nil { + return nil, err + } + + return cmd.Result, nil +} + type FakeDashboardService struct { SaveDashboardResult *models.Dashboard SaveDashboardError error @@ -230,6 +245,10 @@ func (s *FakeDashboardService) SaveDashboard(dto *SaveDashboardDTO) (*models.Das return s.SaveDashboardResult, s.SaveDashboardError } +func (s *FakeDashboardService) ImportDashboard(dto *SaveDashboardDTO) (*models.Dashboard, error) { + return s.SaveDashboard(dto) +} + func MockDashboardService(mock *FakeDashboardService) { NewService = func() DashboardService { return mock From 95ce4725fb819585a73baee23bc1658f3151961f Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 14 Mar 2018 14:48:39 +0100 Subject: [PATCH 7/9] release 5.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 68e9db01749..8ba62f97a25 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Grafana Labs" }, "name": "grafana", - "version": "5.0.1", + "version": "5.0.2", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" From 990168c2afa055dfbdfd7d8f1f3e7631aa90d6d1 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 13 Mar 2018 10:55:43 +0100 Subject: [PATCH 8/9] teams: removes quota on route Got added by mistake a year ago. (cherry picked from commit 3f2c086e6f7ab599dcff1104dc68dff9a3e3fbb9) --- pkg/api/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index d8def3453c4..5b3cde09fd5 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -151,11 +151,11 @@ func (hs *HttpServer) registerRoutes() { apiRoute.Group("/teams", func(teamsRoute RouteRegister) { teamsRoute.Get("/:teamId", wrap(GetTeamById)) teamsRoute.Get("/search", wrap(SearchTeams)) - teamsRoute.Post("/", quota("teams"), bind(m.CreateTeamCommand{}), wrap(CreateTeam)) + teamsRoute.Post("/", bind(m.CreateTeamCommand{}), wrap(CreateTeam)) teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam)) teamsRoute.Delete("/:teamId", wrap(DeleteTeamById)) teamsRoute.Get("/:teamId/members", wrap(GetTeamMembers)) - teamsRoute.Post("/:teamId/members", quota("teams"), bind(m.AddTeamMemberCommand{}), wrap(AddTeamMember)) + teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), wrap(AddTeamMember)) teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember)) }, reqOrgAdmin) From 946a6c7d59cf3ef44fe1419ea9324c37f6aa3a1d Mon Sep 17 00:00:00 2001 From: bergquist Date: Wed, 14 Mar 2018 15:27:18 +0100 Subject: [PATCH 9/9] renderer: avoid redirect render requests closes #11180 (cherry picked from commit 6cac7c2de99d64ab5b2f0fd72aa92e59b2f7ea4d) --- pkg/components/renderer/renderer.go | 4 +++- pkg/middleware/dashboard_redirect.go | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/components/renderer/renderer.go b/pkg/components/renderer/renderer.go index 313f7892707..26751ddd5c7 100644 --- a/pkg/components/renderer/renderer.go +++ b/pkg/components/renderer/renderer.go @@ -72,7 +72,9 @@ func RenderToPng(params *RenderOpts) (string, error) { localDomain = setting.HttpAddr } - url := fmt.Sprintf("%s://%s:%s/%s", setting.Protocol, localDomain, setting.HttpPort, params.Path) + // &render=1 signals to the legacy redirect layer to + // avoid redirect these requests. + url := fmt.Sprintf("%s://%s:%s/%s&render=1", setting.Protocol, localDomain, setting.HttpPort, params.Path) binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, executable)) scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js")) diff --git a/pkg/middleware/dashboard_redirect.go b/pkg/middleware/dashboard_redirect.go index 4a3812fb8a2..c3262253ac9 100644 --- a/pkg/middleware/dashboard_redirect.go +++ b/pkg/middleware/dashboard_redirect.go @@ -6,6 +6,7 @@ import ( "github.com/grafana/grafana/pkg/bus" m "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/setting" "gopkg.in/macaron.v1" ) @@ -36,9 +37,14 @@ func RedirectFromLegacyDashboardUrl() macaron.Handler { func RedirectFromLegacyDashboardSoloUrl() macaron.Handler { return func(c *Context) { slug := c.Params("slug") + renderRequest := c.QueryBool("render") if slug != "" { if url, err := getDashboardUrlBySlug(c.OrgId, slug); err == nil { + if renderRequest && strings.Contains(url, setting.AppSubUrl) { + url = strings.Replace(url, setting.AppSubUrl, "", 1) + } + url = strings.Replace(url, "/d/", "/d-solo/", 1) url = fmt.Sprintf("%s?%s", url, c.Req.URL.RawQuery) c.Redirect(url, 301)