mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Provisioning: Show file path of provisioning file in save/delete dialogs (#16706)
* Add file path to metadata and show it in dialogs * Make path relative to config directory * Fix tests * Add test for the relative path * Refactor to use path relative to provisioner path * Change return types * Rename attribute * Small fixes from review
This commit is contained in:
parent
76ab0aa059
commit
eb82a75668
@ -283,10 +283,10 @@ func (hs *HTTPServer) registerRoutes() {
|
|||||||
|
|
||||||
// Dashboard
|
// Dashboard
|
||||||
apiRoute.Group("/dashboards", func(dashboardRoute routing.RouteRegister) {
|
apiRoute.Group("/dashboards", func(dashboardRoute routing.RouteRegister) {
|
||||||
dashboardRoute.Get("/uid/:uid", Wrap(GetDashboard))
|
dashboardRoute.Get("/uid/:uid", Wrap(hs.GetDashboard))
|
||||||
dashboardRoute.Delete("/uid/:uid", Wrap(DeleteDashboardByUID))
|
dashboardRoute.Delete("/uid/:uid", Wrap(DeleteDashboardByUID))
|
||||||
|
|
||||||
dashboardRoute.Get("/db/:slug", Wrap(GetDashboard))
|
dashboardRoute.Get("/db/:slug", Wrap(hs.GetDashboard))
|
||||||
dashboardRoute.Delete("/db/:slug", Wrap(DeleteDashboardBySlug))
|
dashboardRoute.Delete("/db/:slug", Wrap(DeleteDashboardBySlug))
|
||||||
|
|
||||||
dashboardRoute.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), Wrap(CalculateDashboardDiff))
|
dashboardRoute.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), Wrap(CalculateDashboardDiff))
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
@ -47,7 +48,7 @@ func dashboardGuardianResponse(err error) Response {
|
|||||||
return Error(403, "Access denied to this dashboard", nil)
|
return Error(403, "Access denied to this dashboard", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDashboard(c *m.ReqContext) Response {
|
func (hs *HTTPServer) GetDashboard(c *m.ReqContext) Response {
|
||||||
dash, rsp := getDashboardHelper(c.OrgId, c.Params(":slug"), 0, c.Params(":uid"))
|
dash, rsp := getDashboardHelper(c.OrgId, c.Params(":slug"), 0, c.Params(":uid"))
|
||||||
if rsp != nil {
|
if rsp != nil {
|
||||||
return rsp
|
return rsp
|
||||||
@ -106,14 +107,22 @@ func GetDashboard(c *m.ReqContext) Response {
|
|||||||
meta.FolderUrl = query.Result.GetUrl()
|
meta.FolderUrl = query.Result.GetUrl()
|
||||||
}
|
}
|
||||||
|
|
||||||
isDashboardProvisioned := &m.IsDashboardProvisionedQuery{DashboardId: dash.Id}
|
provisioningData, err := dashboards.NewProvisioningService().GetProvisionedDashboardDataByDashboardId(dash.Id)
|
||||||
err = bus.Dispatch(isDashboardProvisioned)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Error(500, "Error while checking if dashboard is provisioned", err)
|
return Error(500, "Error while checking if dashboard is provisioned", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDashboardProvisioned.Result {
|
if provisioningData != nil {
|
||||||
meta.Provisioned = true
|
meta.Provisioned = true
|
||||||
|
meta.ProvisionedExternalId, err = filepath.Rel(
|
||||||
|
hs.ProvisioningService.GetDashboardProvisionerResolvedPath(provisioningData.Name),
|
||||||
|
provisioningData.ExternalId,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
// Not sure when this could happen so not sure how to better handle this. Right now ProvisionedExternalId
|
||||||
|
// is for better UX, showing in Save/Delete dialogs and so it won't break anything if it is empty.
|
||||||
|
hs.log.Warn("Failed to create ProvisionedExternalId", "err", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure db version is in sync with json model version
|
// make sure db version is in sync with json model version
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
m "github.com/grafana/grafana/pkg/models"
|
m "github.com/grafana/grafana/pkg/models"
|
||||||
"github.com/grafana/grafana/pkg/services/alerting"
|
"github.com/grafana/grafana/pkg/services/alerting"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
|
"github.com/grafana/grafana/pkg/services/provisioning"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
@ -43,8 +44,8 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(query *m.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
query.Result = false
|
query.Result = nil
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -198,8 +199,8 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
fakeDash.HasAcl = true
|
fakeDash.HasAcl = true
|
||||||
setting.ViewersCanEdit = false
|
setting.ViewersCanEdit = false
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(query *m.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
query.Result = false
|
query.Result = nil
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -235,6 +236,10 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
hs := &HTTPServer{
|
||||||
|
Cfg: setting.NewCfg(),
|
||||||
|
}
|
||||||
|
|
||||||
// This tests six scenarios:
|
// This tests six scenarios:
|
||||||
// 1. user is an org viewer AND has no permissions for this dashboard
|
// 1. user is an org viewer AND has no permissions for this dashboard
|
||||||
// 2. user is an org editor AND has no permissions for this dashboard
|
// 2. user is an org editor AND has no permissions for this dashboard
|
||||||
@ -247,7 +252,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
role := m.ROLE_VIEWER
|
role := m.ROLE_VIEWER
|
||||||
|
|
||||||
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
sc.handlerFunc = GetDashboard
|
sc.handlerFunc = hs.GetDashboard
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
|
|
||||||
Convey("Should lookup dashboard by slug", func() {
|
Convey("Should lookup dashboard by slug", func() {
|
||||||
@ -260,7 +265,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
sc.handlerFunc = GetDashboard
|
sc.handlerFunc = hs.GetDashboard
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
|
|
||||||
Convey("Should lookup dashboard by uid", func() {
|
Convey("Should lookup dashboard by uid", func() {
|
||||||
@ -305,7 +310,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
role := m.ROLE_EDITOR
|
role := m.ROLE_EDITOR
|
||||||
|
|
||||||
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/db/child-dash", "/api/dashboards/db/:slug", role, func(sc *scenarioContext) {
|
||||||
sc.handlerFunc = GetDashboard
|
sc.handlerFunc = hs.GetDashboard
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
|
|
||||||
Convey("Should lookup dashboard by slug", func() {
|
Convey("Should lookup dashboard by slug", func() {
|
||||||
@ -318,7 +323,7 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/abcdefghi", "/api/dashboards/uid/:uid", role, func(sc *scenarioContext) {
|
||||||
sc.handlerFunc = GetDashboard
|
sc.handlerFunc = hs.GetDashboard
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
|
|
||||||
Convey("Should lookup dashboard by uid", func() {
|
Convey("Should lookup dashboard by uid", func() {
|
||||||
@ -636,8 +641,8 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
dashTwo.FolderId = 3
|
dashTwo.FolderId = 3
|
||||||
dashTwo.HasAcl = false
|
dashTwo.HasAcl = false
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(query *m.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
query.Result = false
|
query.Result = nil
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -766,8 +771,8 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(query *m.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
query.Result = false
|
query.Result = nil
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -905,12 +910,12 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
|
bus.AddHandler("test", func(query *m.GetDashboardQuery) error {
|
||||||
query.Result = &m.Dashboard{Id: 1}
|
query.Result = &m.Dashboard{Id: 1, Data: &simplejson.Json{}}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *m.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(query *m.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
query.Result = true
|
query.Result = &m.DashboardProvisioning{ExternalId: "/tmp/grafana/dashboards/test/dashboard1.json"}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -940,11 +945,32 @@ func TestDashboardApiEndpoint(t *testing.T) {
|
|||||||
So(result.Get("error").MustString(), ShouldEqual, m.ErrDashboardCannotDeleteProvisionedDashboard.Error())
|
So(result.Get("error").MustString(), ShouldEqual, m.ErrDashboardCannotDeleteProvisionedDashboard.Error())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/uid/dash", "/api/dashboards/uid/:uid", m.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||||
|
mock := provisioning.NewProvisioningServiceMock()
|
||||||
|
mock.GetDashboardProvisionerResolvedPathFunc = func(name string) string {
|
||||||
|
return "/tmp/grafana/dashboards"
|
||||||
|
}
|
||||||
|
|
||||||
|
dash := GetDashboardShouldReturn200WithConfig(sc, mock)
|
||||||
|
|
||||||
|
Convey("Should return relative path to provisioning file", func() {
|
||||||
|
So(dash.Meta.ProvisionedExternalId, ShouldEqual, "test/dashboard1.json")
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta {
|
func GetDashboardShouldReturn200WithConfig(sc *scenarioContext, provisioningService ProvisioningService) dtos.DashboardFullWithMeta {
|
||||||
CallGetDashboard(sc)
|
if provisioningService == nil {
|
||||||
|
provisioningService = provisioning.NewProvisioningServiceMock()
|
||||||
|
}
|
||||||
|
|
||||||
|
hs := &HTTPServer{
|
||||||
|
Cfg: setting.NewCfg(),
|
||||||
|
ProvisioningService: provisioningService,
|
||||||
|
}
|
||||||
|
CallGetDashboard(sc, hs)
|
||||||
|
|
||||||
So(sc.resp.Code, ShouldEqual, 200)
|
So(sc.resp.Code, ShouldEqual, 200)
|
||||||
|
|
||||||
@ -955,8 +981,13 @@ func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta
|
|||||||
return dash
|
return dash
|
||||||
}
|
}
|
||||||
|
|
||||||
func CallGetDashboard(sc *scenarioContext) {
|
func GetDashboardShouldReturn200(sc *scenarioContext) dtos.DashboardFullWithMeta {
|
||||||
sc.handlerFunc = GetDashboard
|
return GetDashboardShouldReturn200WithConfig(sc, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CallGetDashboard(sc *scenarioContext, hs *HTTPServer) {
|
||||||
|
|
||||||
|
sc.handlerFunc = hs.GetDashboard
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,28 +7,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DashboardMeta struct {
|
type DashboardMeta struct {
|
||||||
IsStarred bool `json:"isStarred,omitempty"`
|
IsStarred bool `json:"isStarred,omitempty"`
|
||||||
IsHome bool `json:"isHome,omitempty"`
|
IsHome bool `json:"isHome,omitempty"`
|
||||||
IsSnapshot bool `json:"isSnapshot,omitempty"`
|
IsSnapshot bool `json:"isSnapshot,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
CanSave bool `json:"canSave"`
|
CanSave bool `json:"canSave"`
|
||||||
CanEdit bool `json:"canEdit"`
|
CanEdit bool `json:"canEdit"`
|
||||||
CanAdmin bool `json:"canAdmin"`
|
CanAdmin bool `json:"canAdmin"`
|
||||||
CanStar bool `json:"canStar"`
|
CanStar bool `json:"canStar"`
|
||||||
Slug string `json:"slug"`
|
Slug string `json:"slug"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Expires time.Time `json:"expires"`
|
Expires time.Time `json:"expires"`
|
||||||
Created time.Time `json:"created"`
|
Created time.Time `json:"created"`
|
||||||
Updated time.Time `json:"updated"`
|
Updated time.Time `json:"updated"`
|
||||||
UpdatedBy string `json:"updatedBy"`
|
UpdatedBy string `json:"updatedBy"`
|
||||||
CreatedBy string `json:"createdBy"`
|
CreatedBy string `json:"createdBy"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
HasAcl bool `json:"hasAcl"`
|
HasAcl bool `json:"hasAcl"`
|
||||||
IsFolder bool `json:"isFolder"`
|
IsFolder bool `json:"isFolder"`
|
||||||
FolderId int64 `json:"folderId"`
|
FolderId int64 `json:"folderId"`
|
||||||
FolderTitle string `json:"folderTitle"`
|
FolderTitle string `json:"folderTitle"`
|
||||||
FolderUrl string `json:"folderUrl"`
|
FolderUrl string `json:"folderUrl"`
|
||||||
Provisioned bool `json:"provisioned"`
|
Provisioned bool `json:"provisioned"`
|
||||||
|
ProvisionedExternalId string `json:"provisionedExternalId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DashboardFullWithMeta struct {
|
type DashboardFullWithMeta struct {
|
||||||
|
@ -25,13 +25,12 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/cache"
|
"github.com/grafana/grafana/pkg/services/cache"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
"github.com/grafana/grafana/pkg/services/hooks"
|
"github.com/grafana/grafana/pkg/services/hooks"
|
||||||
"github.com/grafana/grafana/pkg/services/provisioning"
|
|
||||||
"github.com/grafana/grafana/pkg/services/quota"
|
"github.com/grafana/grafana/pkg/services/quota"
|
||||||
"github.com/grafana/grafana/pkg/services/rendering"
|
"github.com/grafana/grafana/pkg/services/rendering"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
macaron "gopkg.in/macaron.v1"
|
"gopkg.in/macaron.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -42,6 +41,13 @@ func init() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProvisioningService interface {
|
||||||
|
ProvisionDatasources() error
|
||||||
|
ProvisionNotifications() error
|
||||||
|
ProvisionDashboards() error
|
||||||
|
GetDashboardProvisionerResolvedPath(name string) string
|
||||||
|
}
|
||||||
|
|
||||||
type HTTPServer struct {
|
type HTTPServer struct {
|
||||||
log log.Logger
|
log log.Logger
|
||||||
macaron *macaron.Macaron
|
macaron *macaron.Macaron
|
||||||
@ -49,17 +55,17 @@ type HTTPServer struct {
|
|||||||
streamManager *live.StreamManager
|
streamManager *live.StreamManager
|
||||||
httpSrv *http.Server
|
httpSrv *http.Server
|
||||||
|
|
||||||
RouteRegister routing.RouteRegister `inject:""`
|
RouteRegister routing.RouteRegister `inject:""`
|
||||||
Bus bus.Bus `inject:""`
|
Bus bus.Bus `inject:""`
|
||||||
RenderService rendering.Service `inject:""`
|
RenderService rendering.Service `inject:""`
|
||||||
Cfg *setting.Cfg `inject:""`
|
Cfg *setting.Cfg `inject:""`
|
||||||
HooksService *hooks.HooksService `inject:""`
|
HooksService *hooks.HooksService `inject:""`
|
||||||
CacheService *cache.CacheService `inject:""`
|
CacheService *cache.CacheService `inject:""`
|
||||||
DatasourceCache datasources.CacheService `inject:""`
|
DatasourceCache datasources.CacheService `inject:""`
|
||||||
AuthTokenService models.UserTokenService `inject:""`
|
AuthTokenService models.UserTokenService `inject:""`
|
||||||
QuotaService *quota.QuotaService `inject:""`
|
QuotaService *quota.QuotaService `inject:""`
|
||||||
RemoteCacheService *remotecache.RemoteCache `inject:""`
|
RemoteCacheService *remotecache.RemoteCache `inject:""`
|
||||||
ProvisioningService provisioning.ProvisioningService `inject:""`
|
ProvisioningService ProvisioningService `inject:""`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hs *HTTPServer) Init() error {
|
func (hs *HTTPServer) Init() error {
|
||||||
|
@ -323,15 +323,13 @@ type GetDashboardSlugByIdQuery struct {
|
|||||||
Result string
|
Result string
|
||||||
}
|
}
|
||||||
|
|
||||||
type IsDashboardProvisionedQuery struct {
|
type GetProvisionedDashboardDataByIdQuery struct {
|
||||||
DashboardId int64
|
DashboardId int64
|
||||||
|
Result *DashboardProvisioning
|
||||||
Result bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetProvisionedDashboardDataQuery struct {
|
type GetProvisionedDashboardDataQuery struct {
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
Result []*DashboardProvisioning
|
Result []*DashboardProvisioning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ type DashboardProvisioningService interface {
|
|||||||
SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
|
SaveProvisionedDashboard(dto *SaveDashboardDTO, provisioning *models.DashboardProvisioning) (*models.Dashboard, error)
|
||||||
SaveFolderForProvisionedDashboards(*SaveDashboardDTO) (*models.Dashboard, error)
|
SaveFolderForProvisionedDashboards(*SaveDashboardDTO) (*models.Dashboard, error)
|
||||||
GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error)
|
GetProvisionedDashboardData(name string) ([]*models.DashboardProvisioning, error)
|
||||||
|
GetProvisionedDashboardDataByDashboardId(dashboardId int64) (*models.DashboardProvisioning, error)
|
||||||
UnprovisionDashboard(dashboardId int64) error
|
UnprovisionDashboard(dashboardId int64) error
|
||||||
DeleteProvisionedDashboard(dashboardId int64, orgId int64) error
|
DeleteProvisionedDashboard(dashboardId int64, orgId int64) error
|
||||||
}
|
}
|
||||||
@ -37,7 +38,9 @@ var NewService = func() DashboardService {
|
|||||||
|
|
||||||
// NewProvisioningService factory for creating a new dashboard provisioning service
|
// NewProvisioningService factory for creating a new dashboard provisioning service
|
||||||
var NewProvisioningService = func() DashboardProvisioningService {
|
var NewProvisioningService = func() DashboardProvisioningService {
|
||||||
return &dashboardServiceImpl{}
|
return &dashboardServiceImpl{
|
||||||
|
log: log.New("dashboard-provisioning-service"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SaveDashboardDTO struct {
|
type SaveDashboardDTO struct {
|
||||||
@ -65,6 +68,16 @@ func (dr *dashboardServiceImpl) GetProvisionedDashboardData(name string) ([]*mod
|
|||||||
return cmd.Result, nil
|
return cmd.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dr *dashboardServiceImpl) GetProvisionedDashboardDataByDashboardId(dashboardId int64) (*models.DashboardProvisioning, error) {
|
||||||
|
cmd := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: dashboardId}
|
||||||
|
err := bus.Dispatch(cmd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd.Result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, validateAlerts bool, validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
|
func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO, validateAlerts bool, validateProvisionedDashboard bool) (*models.SaveDashboardCommand, error) {
|
||||||
dash := dto.Dashboard
|
dash := dto.Dashboard
|
||||||
|
|
||||||
@ -123,14 +136,12 @@ func (dr *dashboardServiceImpl) buildSaveDashboardCommand(dto *SaveDashboardDTO,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if validateProvisionedDashboard {
|
if validateProvisionedDashboard {
|
||||||
isDashboardProvisioned := &models.IsDashboardProvisionedQuery{DashboardId: dash.Id}
|
provisionedData, err := dr.GetProvisionedDashboardDataByDashboardId(dash.Id)
|
||||||
err := bus.Dispatch(isDashboardProvisioned)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDashboardProvisioned.Result {
|
if provisionedData != nil {
|
||||||
return nil, models.ErrDashboardCannotSaveProvisionedDashboard
|
return nil, models.ErrDashboardCannotSaveProvisionedDashboard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,13 +269,12 @@ func (dr *dashboardServiceImpl) DeleteProvisionedDashboard(dashboardId int64, or
|
|||||||
|
|
||||||
func (dr *dashboardServiceImpl) deleteDashboard(dashboardId int64, orgId int64, validateProvisionedDashboard bool) error {
|
func (dr *dashboardServiceImpl) deleteDashboard(dashboardId int64, orgId int64, validateProvisionedDashboard bool) error {
|
||||||
if validateProvisionedDashboard {
|
if validateProvisionedDashboard {
|
||||||
isDashboardProvisioned := &models.IsDashboardProvisionedQuery{DashboardId: dashboardId}
|
provisionedData, err := dr.GetProvisionedDashboardDataByDashboardId(dashboardId)
|
||||||
err := bus.Dispatch(isDashboardProvisioned)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errutil.Wrap("failed to check if dashboard is provisioned", err)
|
return errutil.Wrap("failed to check if dashboard is provisioned", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDashboardProvisioned.Result {
|
if provisionedData != nil {
|
||||||
return models.ErrDashboardCannotDeleteProvisionedDashboard
|
return models.ErrDashboardCannotDeleteProvisionedDashboard
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,8 @@ func TestDashboardService(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
cmd.Result = false
|
cmd.Result = nil
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -85,9 +85,9 @@ func TestDashboardService(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Should return validation error if dashboard is provisioned", func() {
|
Convey("Should return validation error if dashboard is provisioned", func() {
|
||||||
provisioningValidated := false
|
provisioningValidated := false
|
||||||
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
provisioningValidated = true
|
provisioningValidated = true
|
||||||
cmd.Result = true
|
cmd.Result = &models.DashboardProvisioning{}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -109,8 +109,8 @@ func TestDashboardService(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should return validation error if alert data is invalid", func() {
|
Convey("Should return validation error if alert data is invalid", func() {
|
||||||
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
cmd.Result = false
|
cmd.Result = nil
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -129,9 +129,9 @@ func TestDashboardService(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Should not return validation error if dashboard is provisioned", func() {
|
Convey("Should not return validation error if dashboard is provisioned", func() {
|
||||||
provisioningValidated := false
|
provisioningValidated := false
|
||||||
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
provisioningValidated = true
|
provisioningValidated = true
|
||||||
cmd.Result = true
|
cmd.Result = &models.DashboardProvisioning{}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -166,9 +166,9 @@ func TestDashboardService(t *testing.T) {
|
|||||||
|
|
||||||
Convey("Should return validation error if dashboard is provisioned", func() {
|
Convey("Should return validation error if dashboard is provisioned", func() {
|
||||||
provisioningValidated := false
|
provisioningValidated := false
|
||||||
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
provisioningValidated = true
|
provisioningValidated = true
|
||||||
cmd.Result = true
|
cmd.Result = &models.DashboardProvisioning{}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -241,8 +241,12 @@ type Result struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setupDeleteHandlers(provisioned bool) *Result {
|
func setupDeleteHandlers(provisioned bool) *Result {
|
||||||
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
cmd.Result = provisioned
|
if provisioned {
|
||||||
|
cmd.Result = &models.DashboardProvisioning{}
|
||||||
|
} else {
|
||||||
|
cmd.Result = nil
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -112,8 +112,9 @@ func TestFolderService(t *testing.T) {
|
|||||||
|
|
||||||
provisioningValidated := false
|
provisioningValidated := false
|
||||||
|
|
||||||
bus.AddHandler("test", func(query *models.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(query *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
provisioningValidated = true
|
provisioningValidated = true
|
||||||
|
query.Result = nil
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -7,18 +7,11 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DashboardProvisioner interface {
|
|
||||||
Provision() error
|
|
||||||
PollChanges(ctx context.Context)
|
|
||||||
}
|
|
||||||
|
|
||||||
type DashboardProvisionerImpl struct {
|
type DashboardProvisionerImpl struct {
|
||||||
log log.Logger
|
log log.Logger
|
||||||
fileReaders []*fileReader
|
fileReaders []*fileReader
|
||||||
}
|
}
|
||||||
|
|
||||||
type DashboardProvisionerFactory func(string) (DashboardProvisioner, error)
|
|
||||||
|
|
||||||
func NewDashboardProvisionerImpl(configDirectory string) (*DashboardProvisionerImpl, error) {
|
func NewDashboardProvisionerImpl(configDirectory string) (*DashboardProvisionerImpl, error) {
|
||||||
logger := log.New("provisioning.dashboard")
|
logger := log.New("provisioning.dashboard")
|
||||||
cfgReader := &configReader{path: configDirectory, log: logger}
|
cfgReader := &configReader{path: configDirectory, log: logger}
|
||||||
@ -61,6 +54,17 @@ func (provider *DashboardProvisionerImpl) PollChanges(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetProvisionerResolvedPath returns resolved path for the specified provisioner name. Can be used to generate
|
||||||
|
// relative path to provisioning file from it's external_id.
|
||||||
|
func (provider *DashboardProvisionerImpl) GetProvisionerResolvedPath(name string) string {
|
||||||
|
for _, reader := range provider.fileReaders {
|
||||||
|
if reader.Cfg.Name == name {
|
||||||
|
return reader.resolvedPath()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func getFileReaders(configs []*DashboardsAsConfig, logger log.Logger) ([]*fileReader, error) {
|
func getFileReaders(configs []*DashboardsAsConfig, logger log.Logger) ([]*fileReader, error) {
|
||||||
var readers []*fileReader
|
var readers []*fileReader
|
||||||
|
|
||||||
|
@ -3,14 +3,16 @@ package dashboards
|
|||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
type Calls struct {
|
type Calls struct {
|
||||||
Provision []interface{}
|
Provision []interface{}
|
||||||
PollChanges []interface{}
|
PollChanges []interface{}
|
||||||
|
GetProvisionerResolvedPath []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type DashboardProvisionerMock struct {
|
type DashboardProvisionerMock struct {
|
||||||
Calls *Calls
|
Calls *Calls
|
||||||
ProvisionFunc func() error
|
ProvisionFunc func() error
|
||||||
PollChangesFunc func(ctx context.Context)
|
PollChangesFunc func(ctx context.Context)
|
||||||
|
GetProvisionerResolvedPathFunc func(name string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDashboardProvisionerMock() *DashboardProvisionerMock {
|
func NewDashboardProvisionerMock() *DashboardProvisionerMock {
|
||||||
@ -34,3 +36,12 @@ func (dpm *DashboardProvisionerMock) PollChanges(ctx context.Context) {
|
|||||||
dpm.PollChangesFunc(ctx)
|
dpm.PollChangesFunc(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dpm *DashboardProvisionerMock) GetProvisionerResolvedPath(name string) string {
|
||||||
|
dpm.Calls.PollChanges = append(dpm.Calls.GetProvisionerResolvedPath, name)
|
||||||
|
if dpm.GetProvisionerResolvedPathFunc != nil {
|
||||||
|
return dpm.GetProvisionerResolvedPathFunc(name)
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -70,7 +70,7 @@ func (fr *fileReader) pollChanges(ctx context.Context) {
|
|||||||
// to the database.
|
// to the database.
|
||||||
func (fr *fileReader) startWalkingDisk() error {
|
func (fr *fileReader) startWalkingDisk() error {
|
||||||
fr.log.Debug("Start walking disk", "path", fr.Path)
|
fr.log.Debug("Start walking disk", "path", fr.Path)
|
||||||
resolvedPath := fr.resolvePath(fr.Path)
|
resolvedPath := fr.resolvedPath()
|
||||||
if _, err := os.Stat(resolvedPath); err != nil {
|
if _, err := os.Stat(resolvedPath); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return err
|
return err
|
||||||
@ -329,24 +329,23 @@ func (fr *fileReader) readDashboardFromFile(path string, lastModified time.Time,
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fr *fileReader) resolvePath(path string) string {
|
func (fr *fileReader) resolvedPath() string {
|
||||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
if _, err := os.Stat(fr.Path); os.IsNotExist(err) {
|
||||||
fr.log.Error("Cannot read directory", "error", err)
|
fr.log.Error("Cannot read directory", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
copy := path
|
path, err := filepath.Abs(fr.Path)
|
||||||
path, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fr.log.Error("Could not create absolute path", "path", copy, "error", err)
|
fr.log.Error("Could not create absolute path", "path", fr.Path, "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
path, err = filepath.EvalSymlinks(path)
|
path, err = filepath.EvalSymlinks(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fr.log.Error("Failed to read content of symlinked path", "path", copy, "error", err)
|
fr.log.Error("Failed to read content of symlinked path", "path", fr.Path, "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = copy
|
path = fr.Path
|
||||||
fr.log.Info("falling back to original path due to EvalSymlink/Abs failure")
|
fr.log.Info("falling back to original path due to EvalSymlink/Abs failure")
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
|
@ -33,7 +33,7 @@ func TestProvsionedSymlinkedFolder(t *testing.T) {
|
|||||||
t.Errorf("expected err to be nil")
|
t.Errorf("expected err to be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvedPath := reader.resolvePath(reader.Path)
|
resolvedPath := reader.resolvedPath()
|
||||||
if resolvedPath != want {
|
if resolvedPath != want {
|
||||||
t.Errorf("got %s want %s", resolvedPath, want)
|
t.Errorf("got %s want %s", resolvedPath, want)
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ func TestCreatingNewDashboardFileReader(t *testing.T) {
|
|||||||
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"))
|
reader, err := NewDashboardFileReader(cfg, log.New("test-logger"))
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
resolvedPath := reader.resolvePath(reader.Path)
|
resolvedPath := reader.resolvedPath()
|
||||||
So(filepath.IsAbs(resolvedPath), ShouldBeTrue)
|
So(filepath.IsAbs(resolvedPath), ShouldBeTrue)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -435,6 +435,10 @@ func (s *fakeDashboardProvisioningService) DeleteProvisionedDashboard(dashboardI
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *fakeDashboardProvisioningService) GetProvisionedDashboardDataByDashboardId(dashboardId int64) (*models.DashboardProvisioning, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func mockGetDashboardQuery(cmd *models.GetDashboardQuery) error {
|
func mockGetDashboardQuery(cmd *models.GetDashboardQuery) error {
|
||||||
for _, d := range fakeService.getDashboard {
|
for _, d := range fakeService.getDashboard {
|
||||||
if d.Slug == cmd.Slug {
|
if d.Slug == cmd.Slug {
|
||||||
|
@ -15,9 +15,17 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type DashboardProvisioner interface {
|
||||||
|
Provision() error
|
||||||
|
PollChanges(ctx context.Context)
|
||||||
|
GetProvisionerResolvedPath(name string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DashboardProvisionerFactory func(string) (DashboardProvisioner, error)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registry.RegisterService(NewProvisioningServiceImpl(
|
registry.RegisterService(NewProvisioningServiceImpl(
|
||||||
func(path string) (dashboards.DashboardProvisioner, error) {
|
func(path string) (DashboardProvisioner, error) {
|
||||||
return dashboards.NewDashboardProvisionerImpl(path)
|
return dashboards.NewDashboardProvisionerImpl(path)
|
||||||
},
|
},
|
||||||
notifiers.Provision,
|
notifiers.Provision,
|
||||||
@ -25,14 +33,8 @@ func init() {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProvisioningService interface {
|
|
||||||
ProvisionDatasources() error
|
|
||||||
ProvisionNotifications() error
|
|
||||||
ProvisionDashboards() error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewProvisioningServiceImpl(
|
func NewProvisioningServiceImpl(
|
||||||
newDashboardProvisioner dashboards.DashboardProvisionerFactory,
|
newDashboardProvisioner DashboardProvisionerFactory,
|
||||||
provisionNotifiers func(string) error,
|
provisionNotifiers func(string) error,
|
||||||
provisionDatasources func(string) error,
|
provisionDatasources func(string) error,
|
||||||
) *provisioningServiceImpl {
|
) *provisioningServiceImpl {
|
||||||
@ -48,8 +50,8 @@ type provisioningServiceImpl struct {
|
|||||||
Cfg *setting.Cfg `inject:""`
|
Cfg *setting.Cfg `inject:""`
|
||||||
log log.Logger
|
log log.Logger
|
||||||
pollingCtxCancel context.CancelFunc
|
pollingCtxCancel context.CancelFunc
|
||||||
newDashboardProvisioner dashboards.DashboardProvisionerFactory
|
newDashboardProvisioner DashboardProvisionerFactory
|
||||||
dashboardProvisioner dashboards.DashboardProvisioner
|
dashboardProvisioner DashboardProvisioner
|
||||||
provisionNotifiers func(string) error
|
provisionNotifiers func(string) error
|
||||||
provisionDatasources func(string) error
|
provisionDatasources func(string) error
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
@ -131,6 +133,10 @@ func (ps *provisioningServiceImpl) ProvisionDashboards() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ps *provisioningServiceImpl) GetDashboardProvisionerResolvedPath(name string) string {
|
||||||
|
return ps.dashboardProvisioner.GetProvisionerResolvedPath(name)
|
||||||
|
}
|
||||||
|
|
||||||
func (ps *provisioningServiceImpl) cancelPolling() {
|
func (ps *provisioningServiceImpl) cancelPolling() {
|
||||||
if ps.pollingCtxCancel != nil {
|
if ps.pollingCtxCancel != nil {
|
||||||
ps.log.Debug("Stop polling for dashboard changes")
|
ps.log.Debug("Stop polling for dashboard changes")
|
||||||
|
58
pkg/services/provisioning/provisioning_mock.go
Normal file
58
pkg/services/provisioning/provisioning_mock.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package provisioning
|
||||||
|
|
||||||
|
type Calls struct {
|
||||||
|
ProvisionDatasources []interface{}
|
||||||
|
ProvisionNotifications []interface{}
|
||||||
|
ProvisionDashboards []interface{}
|
||||||
|
GetDashboardProvisionerResolvedPath []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProvisioningServiceMock struct {
|
||||||
|
Calls *Calls
|
||||||
|
ProvisionDatasourcesFunc func() error
|
||||||
|
ProvisionNotificationsFunc func() error
|
||||||
|
ProvisionDashboardsFunc func() error
|
||||||
|
GetDashboardProvisionerResolvedPathFunc func(name string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProvisioningServiceMock() *ProvisioningServiceMock {
|
||||||
|
return &ProvisioningServiceMock{
|
||||||
|
Calls: &Calls{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mock *ProvisioningServiceMock) ProvisionDatasources() error {
|
||||||
|
mock.Calls.ProvisionDatasources = append(mock.Calls.ProvisionDatasources, nil)
|
||||||
|
if mock.ProvisionDatasourcesFunc != nil {
|
||||||
|
return mock.ProvisionDatasourcesFunc()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mock *ProvisioningServiceMock) ProvisionNotifications() error {
|
||||||
|
mock.Calls.ProvisionNotifications = append(mock.Calls.ProvisionNotifications, nil)
|
||||||
|
if mock.ProvisionNotificationsFunc != nil {
|
||||||
|
return mock.ProvisionNotificationsFunc()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mock *ProvisioningServiceMock) ProvisionDashboards() error {
|
||||||
|
mock.Calls.ProvisionDashboards = append(mock.Calls.ProvisionDashboards, nil)
|
||||||
|
if mock.ProvisionDashboardsFunc != nil {
|
||||||
|
return mock.ProvisionDashboardsFunc()
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mock *ProvisioningServiceMock) GetDashboardProvisionerResolvedPath(name string) string {
|
||||||
|
mock.Calls.GetDashboardProvisionerResolvedPath = append(mock.Calls.GetDashboardProvisionerResolvedPath, name)
|
||||||
|
if mock.GetDashboardProvisionerResolvedPathFunc != nil {
|
||||||
|
return mock.GetDashboardProvisionerResolvedPathFunc(name)
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
@ -92,7 +92,7 @@ func setup() *serviceTestStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
serviceTest.service = NewProvisioningServiceImpl(
|
serviceTest.service = NewProvisioningServiceImpl(
|
||||||
func(path string) (dashboards.DashboardProvisioner, error) {
|
func(path string) (DashboardProvisioner, error) {
|
||||||
return serviceTest.mock, nil
|
return serviceTest.mock, nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -19,16 +19,16 @@ type DashboardExtras struct {
|
|||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetProvisionedDataByDashboardId(cmd *models.IsDashboardProvisionedQuery) error {
|
func GetProvisionedDataByDashboardId(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
result := &models.DashboardProvisioning{}
|
result := &models.DashboardProvisioning{}
|
||||||
|
|
||||||
exist, err := x.Where("dashboard_id = ?", cmd.DashboardId).Get(result)
|
exist, err := x.Where("dashboard_id = ?", cmd.DashboardId).Get(result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if exist {
|
||||||
cmd.Result = exist
|
cmd.Result = result
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,20 +65,20 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can query for one provisioned dashboard", func() {
|
Convey("Can query for one provisioned dashboard", func() {
|
||||||
query := &models.IsDashboardProvisionedQuery{DashboardId: cmd.Result.Id}
|
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: cmd.Result.Id}
|
||||||
|
|
||||||
err := GetProvisionedDataByDashboardId(query)
|
err := GetProvisionedDataByDashboardId(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
So(query.Result, ShouldBeTrue)
|
So(query.Result, ShouldNotBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Can query for none provisioned dashboard", func() {
|
Convey("Can query for none provisioned dashboard", func() {
|
||||||
query := &models.IsDashboardProvisionedQuery{DashboardId: 3000}
|
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: 3000}
|
||||||
|
|
||||||
err := GetProvisionedDataByDashboardId(query)
|
err := GetProvisionedDataByDashboardId(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result, ShouldBeFalse)
|
So(query.Result, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Deleting folder should delete provision meta data", func() {
|
Convey("Deleting folder should delete provision meta data", func() {
|
||||||
@ -89,11 +89,11 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
|||||||
|
|
||||||
So(DeleteDashboard(deleteCmd), ShouldBeNil)
|
So(DeleteDashboard(deleteCmd), ShouldBeNil)
|
||||||
|
|
||||||
query := &models.IsDashboardProvisionedQuery{DashboardId: cmd.Result.Id}
|
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: cmd.Result.Id}
|
||||||
|
|
||||||
err = GetProvisionedDataByDashboardId(query)
|
err = GetProvisionedDataByDashboardId(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result, ShouldBeFalse)
|
So(query.Result, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("UnprovisionDashboard should delete provisioning metadata", func() {
|
Convey("UnprovisionDashboard should delete provisioning metadata", func() {
|
||||||
@ -103,11 +103,11 @@ func TestDashboardProvisioningTest(t *testing.T) {
|
|||||||
|
|
||||||
So(UnprovisionDashboard(unprovisionCmd), ShouldBeNil)
|
So(UnprovisionDashboard(unprovisionCmd), ShouldBeNil)
|
||||||
|
|
||||||
query := &models.IsDashboardProvisionedQuery{DashboardId: dashId}
|
query := &models.GetProvisionedDashboardDataByIdQuery{DashboardId: dashId}
|
||||||
|
|
||||||
err = GetProvisionedDataByDashboardId(query)
|
err = GetProvisionedDataByDashboardId(query)
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(query.Result, ShouldBeFalse)
|
So(query.Result, ShouldBeNil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -27,8 +27,8 @@ func TestIntegratedDashboardService(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
bus.AddHandler("test", func(cmd *models.IsDashboardProvisionedQuery) error {
|
bus.AddHandler("test", func(cmd *models.GetProvisionedDashboardDataByIdQuery) error {
|
||||||
cmd.Result = false
|
cmd.Result = nil
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -192,6 +192,8 @@ export class SettingsCtrl {
|
|||||||
text2: `
|
text2: `
|
||||||
<i>See <a class="external-link" href="http://docs.grafana.org/administration/provisioning/#dashboards" target="_blank">
|
<i>See <a class="external-link" href="http://docs.grafana.org/administration/provisioning/#dashboards" target="_blank">
|
||||||
documentation</a> for more information about provisioning.</i>
|
documentation</a> for more information about provisioning.</i>
|
||||||
|
</br>
|
||||||
|
File path: ${this.dashboard.meta.provisionedExternalId}
|
||||||
`,
|
`,
|
||||||
text2htmlBind: true,
|
text2htmlBind: true,
|
||||||
icon: 'fa-trash',
|
icon: 'fa-trash',
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
|
import { DashboardModel } from '../../state';
|
||||||
|
|
||||||
const template = `
|
const template = `
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
@ -21,6 +22,9 @@ const template = `
|
|||||||
<i>See <a class="external-link" href="http://docs.grafana.org/administration/provisioning/#dashboards" target="_blank">
|
<i>See <a class="external-link" href="http://docs.grafana.org/administration/provisioning/#dashboards" target="_blank">
|
||||||
documentation</a> for more information about provisioning.</i>
|
documentation</a> for more information about provisioning.</i>
|
||||||
</small>
|
</small>
|
||||||
|
<div class="p-t-1">
|
||||||
|
File path: {{ctrl.dashboardModel.meta.provisionedExternalId}}
|
||||||
|
</div>
|
||||||
<div class="p-t-2">
|
<div class="p-t-2">
|
||||||
<div class="gf-form">
|
<div class="gf-form">
|
||||||
<code-editor content="ctrl.dashboardJson" data-mode="json" data-max-lines=15></code-editor>
|
<code-editor content="ctrl.dashboardJson" data-mode="json" data-max-lines=15></code-editor>
|
||||||
@ -41,12 +45,14 @@ const template = `
|
|||||||
|
|
||||||
export class SaveProvisionedDashboardModalCtrl {
|
export class SaveProvisionedDashboardModalCtrl {
|
||||||
dash: any;
|
dash: any;
|
||||||
|
dashboardModel: DashboardModel;
|
||||||
dashboardJson: string;
|
dashboardJson: string;
|
||||||
dismiss: () => void;
|
dismiss: () => void;
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor(dashboardSrv) {
|
constructor(dashboardSrv) {
|
||||||
this.dash = dashboardSrv.getCurrent().getSaveModelClone();
|
this.dashboardModel = dashboardSrv.getCurrent();
|
||||||
|
this.dash = this.dashboardModel.getSaveModelClone();
|
||||||
delete this.dash.id;
|
delete this.dash.id;
|
||||||
this.dashboardJson = angular.toJson(this.dash, true);
|
this.dashboardJson = angular.toJson(this.dash, true);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ export interface DashboardMeta {
|
|||||||
canMakeEditable?: boolean;
|
canMakeEditable?: boolean;
|
||||||
submenuEnabled?: boolean;
|
submenuEnabled?: boolean;
|
||||||
provisioned?: boolean;
|
provisioned?: boolean;
|
||||||
|
provisionedExternalId?: string;
|
||||||
focusPanelId?: number;
|
focusPanelId?: number;
|
||||||
isStarred?: boolean;
|
isStarred?: boolean;
|
||||||
showSettings?: boolean;
|
showSettings?: boolean;
|
||||||
|
Loading…
Reference in New Issue
Block a user