From 9d6da10e82ff254c8877f8ea2405934493b09541 Mon Sep 17 00:00:00 2001 From: Victor Cinaglia Date: Mon, 10 Dec 2018 16:36:32 -0500 Subject: [PATCH] snapshots: Move external snapshot creation to backend --- pkg/api/dashboard_snapshot.go | 82 +++++++++++++++++-- pkg/models/dashboard_snapshot.go | 22 +++-- pkg/services/sqlstore/dashboard_snapshot.go | 22 ++--- .../features/dashboard/share_snapshot_ctrl.ts | 33 +------- 4 files changed, 103 insertions(+), 56 deletions(-) diff --git a/pkg/api/dashboard_snapshot.go b/pkg/api/dashboard_snapshot.go index e4e9c9d040f..6c3ee7b69c6 100644 --- a/pkg/api/dashboard_snapshot.go +++ b/pkg/api/dashboard_snapshot.go @@ -1,10 +1,15 @@ package api import ( + "bytes" + "encoding/json" + "fmt" + "net/http" "time" "github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/bus" + "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/metrics" m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/guardian" @@ -12,6 +17,11 @@ import ( "github.com/grafana/grafana/pkg/util" ) +var client = &http.Client{ + Timeout: time.Second * 5, + Transport: &http.Transport{Proxy: http.ProxyFromEnvironment}, +} + func GetSharingOptions(c *m.ReqContext) { c.JSON(200, util.DynMap{ "externalSnapshotURL": setting.ExternalSnapshotUrl, @@ -20,26 +30,82 @@ func GetSharingOptions(c *m.ReqContext) { }) } +type CreateExternalSnapshotResponse struct { + Key string `json:"key"` + DeleteKey string `json:"deleteKey"` + Url string `json:"url"` + DeleteUrl string `json:"deleteUrl"` +} + +func createExternalDashboardSnapshot(cmd m.CreateDashboardSnapshotCommand) (*CreateExternalSnapshotResponse, error) { + var createSnapshotResponse CreateExternalSnapshotResponse + message := map[string]interface{}{ + "name": cmd.Name, + "expires": cmd.Expires, + "dashboard": cmd.Dashboard, + } + + messageBytes, err := simplejson.NewFromAny(message).Encode() + if err != nil { + return nil, err + } + + response, err := client.Post(setting.ExternalSnapshotUrl+"/api/snapshots", "application/json", bytes.NewBuffer(messageBytes)) + if response != nil { + defer response.Body.Close() + } + + if err != nil { + return nil, err + } + + if response.StatusCode != 200 { + return nil, fmt.Errorf("Create external snapshot response status code %d", response.StatusCode) + } + + if err := json.NewDecoder(response.Body).Decode(&createSnapshotResponse); err != nil { + return nil, err + } + + return &createSnapshotResponse, nil +} + +// POST /api/snapshots func CreateDashboardSnapshot(c *m.ReqContext, cmd m.CreateDashboardSnapshotCommand) { if cmd.Name == "" { cmd.Name = "Unnamed snapshot" } + var url string + cmd.ExternalUrl = "" + cmd.OrgId = c.OrgId + cmd.UserId = c.UserId + if cmd.External { - // external snapshot ref requires key and delete key - if cmd.Key == "" || cmd.DeleteKey == "" { - c.JsonApiErr(400, "Missing key and delete key for external snapshot", nil) + if !setting.ExternalEnabled { + c.JsonApiErr(403, "External dashboard creation is disabled", nil) return } - cmd.OrgId = -1 - cmd.UserId = -1 + response, err := createExternalDashboardSnapshot(cmd) + if err != nil { + c.JsonApiErr(500, "Failed to create external snaphost", err) + return + } + + url = response.Url + cmd.Key = response.Key + cmd.DeleteKey = response.DeleteKey + cmd.ExternalUrl = response.Url + cmd.ExternalDeleteUrl = response.DeleteUrl + cmd.Dashboard = simplejson.New() + metrics.M_Api_Dashboard_Snapshot_External.Inc() } else { cmd.Key = util.GetRandomString(32) cmd.DeleteKey = util.GetRandomString(32) - cmd.OrgId = c.OrgId - cmd.UserId = c.UserId + url = setting.ToAbsUrl("dashboard/snapshot/" + cmd.Key) + metrics.M_Api_Dashboard_Snapshot_Create.Inc() } @@ -51,7 +117,7 @@ func CreateDashboardSnapshot(c *m.ReqContext, cmd m.CreateDashboardSnapshotComma c.JSON(200, util.DynMap{ "key": cmd.Key, "deleteKey": cmd.DeleteKey, - "url": setting.ToAbsUrl("dashboard/snapshot/" + cmd.Key), + "url": url, "deleteUrl": setting.ToAbsUrl("api/snapshots-delete/" + cmd.DeleteKey), }) } diff --git a/pkg/models/dashboard_snapshot.go b/pkg/models/dashboard_snapshot.go index 3024ba94122..e8db0372758 100644 --- a/pkg/models/dashboard_snapshot.go +++ b/pkg/models/dashboard_snapshot.go @@ -8,14 +8,15 @@ import ( // DashboardSnapshot model type DashboardSnapshot struct { - Id int64 - Name string - Key string - DeleteKey string - OrgId int64 - UserId int64 - External bool - ExternalUrl string + Id int64 + Name string + Key string + DeleteKey string + OrgId int64 + UserId int64 + External bool + ExternalUrl string + ExternalDeleteUrl string Expires time.Time Created time.Time @@ -48,7 +49,10 @@ type CreateDashboardSnapshotCommand struct { Expires int64 `json:"expires"` // these are passed when storing an external snapshot ref - External bool `json:"external"` + External bool `json:"external"` + ExternalUrl string `json:"-"` + ExternalDeleteUrl string `json:"-"` + Key string `json:"key"` DeleteKey string `json:"deleteKey"` diff --git a/pkg/services/sqlstore/dashboard_snapshot.go b/pkg/services/sqlstore/dashboard_snapshot.go index 2e2ea8a4783..d0af676a305 100644 --- a/pkg/services/sqlstore/dashboard_snapshot.go +++ b/pkg/services/sqlstore/dashboard_snapshot.go @@ -47,16 +47,18 @@ func CreateDashboardSnapshot(cmd *m.CreateDashboardSnapshotCommand) error { } snapshot := &m.DashboardSnapshot{ - Name: cmd.Name, - Key: cmd.Key, - DeleteKey: cmd.DeleteKey, - OrgId: cmd.OrgId, - UserId: cmd.UserId, - External: cmd.External, - Dashboard: cmd.Dashboard, - Expires: expires, - Created: time.Now(), - Updated: time.Now(), + Name: cmd.Name, + Key: cmd.Key, + DeleteKey: cmd.DeleteKey, + OrgId: cmd.OrgId, + UserId: cmd.UserId, + External: cmd.External, + ExternalUrl: cmd.ExternalUrl, + ExternalDeleteUrl: cmd.ExternalDeleteUrl, + Dashboard: cmd.Dashboard, + Expires: expires, + Created: time.Now(), + Updated: time.Now(), } _, err := sess.Insert(snapshot) diff --git a/public/app/features/dashboard/share_snapshot_ctrl.ts b/public/app/features/dashboard/share_snapshot_ctrl.ts index ac09d63054d..7dcf0469e77 100644 --- a/public/app/features/dashboard/share_snapshot_ctrl.ts +++ b/public/app/features/dashboard/share_snapshot_ctrl.ts @@ -27,7 +27,6 @@ export class ShareSnapshotCtrl { $scope.init = () => { backendSrv.get('/api/snapshot/shared-options').then(options => { - $scope.externalUrl = options['externalSnapshotURL']; $scope.sharingButtonText = options['externalSnapshotName']; $scope.externalEnabled = options['externalEnabled']; }); @@ -61,30 +60,14 @@ export class ShareSnapshotCtrl { dashboard: dash, name: dash.title, expires: $scope.snapshot.expires, + external: external, }; - const postUrl = external ? $scope.externalUrl + $scope.apiUrl : $scope.apiUrl; - - backendSrv.post(postUrl, cmdData).then( + backendSrv.post($scope.apiUrl, cmdData).then( results => { $scope.loading = false; - - if (external) { - $scope.deleteUrl = results.deleteUrl; - $scope.snapshotUrl = results.url; - $scope.saveExternalSnapshotRef(cmdData, results); - } else { - const url = $location.url(); - let baseUrl = $location.absUrl(); - - if (url !== '/') { - baseUrl = baseUrl.replace(url, '') + '/'; - } - - $scope.snapshotUrl = baseUrl + 'dashboard/snapshot/' + results.key; - $scope.deleteUrl = baseUrl + 'api/snapshots-delete/' + results.deleteKey; - } - + $scope.deleteUrl = results.deleteUrl; + $scope.snapshotUrl = results.url; $scope.step = 2; }, () => { @@ -161,14 +144,6 @@ export class ShareSnapshotCtrl { $scope.step = 3; }); }; - - $scope.saveExternalSnapshotRef = (cmdData, results) => { - // save external in local instance as well - cmdData.external = true; - cmdData.key = results.key; - cmdData.deleteKey = results.deleteKey; - backendSrv.post('/api/snapshots/', cmdData); - }; } }