From 703f728c0c87812f0c57e467d22a6850f899cc76 Mon Sep 17 00:00:00 2001 From: Carl Bergquist Date: Mon, 22 Jun 2020 18:00:39 +0200 Subject: [PATCH] Dashboards: Make path to default dashboard configurable (#25595) Closes #25463 Co-authored-by: Arve Knudsen Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com> --- conf/defaults.ini | 3 ++ conf/sample.ini | 3 ++ docs/sources/installation/configuration.md | 5 +++ pkg/api/api.go | 2 +- pkg/api/dashboard.go | 14 ++++--- pkg/api/dashboard_test.go | 49 ++++++++++++++++++++++ pkg/setting/setting.go | 7 ++++ 7 files changed, 77 insertions(+), 6 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 545e5cd652a..68a0d614383 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -234,6 +234,9 @@ versions_to_keep = 20 # The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. min_refresh_interval = 5s +# Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json" +default_home_dashboard_path = + #################################### Users ############################### [users] # disable user signup / registration diff --git a/conf/sample.ini b/conf/sample.ini index 5fa77743736..3adadf10139 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -233,6 +233,9 @@ # The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. 30s or 1m. ;min_refresh_interval = 5s +# Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json" +;default_home_dashboard_path = + #################################### Users ############################### [users] # disable user signup / registration diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index 11a26d47b3b..459177041f9 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -525,6 +525,11 @@ Number dashboard versions to keep (per dashboard). Default: `20`, Minimum: `1`. This will restrict users to set the refresh interval of a dashboard lower than given interval. Per default this is 5 seconds. The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. `30s` or `1m`. +### default_home_dashboard_path + +Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json" + + ## [dashboards.json] > This have been replaced with dashboards [provisioning]({{< relref "../administration/provisioning" >}}) in 5.0+ diff --git a/pkg/api/api.go b/pkg/api/api.go index 44de1db8bdf..14de9043c53 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -298,7 +298,7 @@ func (hs *HTTPServer) registerRoutes() { dashboardRoute.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), Wrap(CalculateDashboardDiff)) dashboardRoute.Post("/db", bind(models.SaveDashboardCommand{}), Wrap(hs.PostDashboard)) - dashboardRoute.Get("/home", Wrap(GetHomeDashboard)) + dashboardRoute.Get("/home", Wrap(hs.GetHomeDashboard)) dashboardRoute.Get("/tags", GetDashboardTags) dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), Wrap(ImportDashboard)) diff --git a/pkg/api/dashboard.go b/pkg/api/dashboard.go index f1b315efc3f..2ec3bfd3d07 100644 --- a/pkg/api/dashboard.go +++ b/pkg/api/dashboard.go @@ -19,7 +19,6 @@ import ( "github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/services/guardian" - "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/util" ) @@ -315,15 +314,16 @@ func dashboardSaveErrorToApiResponse(err error) Response { return Error(500, "Failed to save dashboard", err) } -func GetHomeDashboard(c *models.ReqContext) Response { +// GetHomeDashboard returns the home dashboard. +func (hs *HTTPServer) GetHomeDashboard(c *models.ReqContext) Response { prefsQuery := models.GetPreferencesWithDefaultsQuery{User: c.SignedInUser} - if err := bus.Dispatch(&prefsQuery); err != nil { + if err := hs.Bus.Dispatch(&prefsQuery); err != nil { return Error(500, "Failed to get preferences", err) } if prefsQuery.Result.HomeDashboardId != 0 { slugQuery := models.GetDashboardRefByIdQuery{Id: prefsQuery.Result.HomeDashboardId} - err := bus.Dispatch(&slugQuery) + err := hs.Bus.Dispatch(&slugQuery) if err == nil { url := models.GetDashboardUrl(slugQuery.Result.Uid, slugQuery.Result.Slug) dashRedirect := dtos.DashboardRedirect{RedirectUri: url} @@ -332,7 +332,11 @@ func GetHomeDashboard(c *models.ReqContext) Response { log.Warn("Failed to get slug from database, %s", err.Error()) } - filePath := path.Join(setting.StaticRootPath, "dashboards/home.json") + filePath := hs.Cfg.DefaultHomeDashboardPath + if filePath == "" { + filePath = path.Join(hs.Cfg.StaticRootPath, "dashboards/home.json") + } + file, err := os.Open(filePath) if err != nil { return Error(500, "Failed to load home dashboard", err) diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index 5c490d34620..b58cf217d3a 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" "fmt" + "io/ioutil" "testing" "github.com/grafana/grafana/pkg/api/dtos" @@ -13,10 +14,58 @@ import ( "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/provisioning" "github.com/grafana/grafana/pkg/setting" + "github.com/stretchr/testify/require" . "github.com/smartystreets/goconvey/convey" ) +func TestGetHomeDashboard(t *testing.T) { + req := &models.ReqContext{SignedInUser: &models.SignedInUser{}} + cfg := setting.NewCfg() + cfg.StaticRootPath = "../../public/" + + hs := &HTTPServer{Cfg: cfg, Bus: bus.New()} + hs.Bus.AddHandler(func(query *models.GetPreferencesWithDefaultsQuery) error { + query.Result = &models.Preferences{ + HomeDashboardId: 0, + } + return nil + }) + + tests := []struct { + name string + defaultSetting string + expectedDashboardPath string + }{ + {name: "using default config", defaultSetting: "", expectedDashboardPath: "../../public/dashboards/home.json"}, + {name: "custom path", defaultSetting: "../../public/dashboards/default.json", expectedDashboardPath: "../../public/dashboards/default.json"}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + dash := dtos.DashboardFullWithMeta{} + dash.Meta.IsHome = true + dash.Meta.FolderTitle = "General" + + homeDashJSON, err := ioutil.ReadFile(tc.expectedDashboardPath) + require.NoError(t, err, "must be able to read expected dashboard file") + hs.Cfg.DefaultHomeDashboardPath = tc.defaultSetting + bytes, err := simplejson.NewJson(homeDashJSON) + require.NoError(t, err, "must be able to encode file as JSON") + + dash.Dashboard = bytes + + b, err := json.Marshal(dash) + require.NoError(t, err, "must be able to marshal object to JSON") + + res := hs.GetHomeDashboard(req) + nr, ok := res.(*NormalResponse) + require.True(t, ok, "should return *NormalResponse") + require.Equal(t, b, nr.body, "default home dashboard should equal content on disk") + }) + } +} + // This tests three main scenarios. // If a user has access to execute an action on a dashboard: // 1. and the dashboard is in a folder which does not have an acl diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index 17c53892621..e6df51b26da 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -227,6 +227,7 @@ type Cfg struct { AppUrl string AppSubUrl string ServeFromSubPath bool + StaticRootPath string // build BuildVersion string @@ -272,6 +273,9 @@ type Cfg struct { DisableSanitizeHtml bool EnterpriseLicensePath string + // Dashboards + DefaultHomeDashboardPath string + // Auth LoginCookieName string LoginMaxInactiveLifetimeDays int @@ -699,6 +703,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { return err } StaticRootPath = makeAbsolute(staticRoot, HomePath) + cfg.StaticRootPath = StaticRootPath if err := cfg.validateStaticRootPath(); err != nil { return err @@ -778,6 +783,8 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { return err } + cfg.DefaultHomeDashboardPath = dashboards.Key("default_home_dashboard_path").MustString("") + // read data source proxy white list DataProxyWhiteList = make(map[string]bool) securityStr, err := valueAsString(security, "data_source_proxy_whitelist", "")