Backend: Migrate to using non-global configuration (#31856)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Arve Knudsen
2021-03-10 12:41:29 +01:00
committed by GitHub
parent bac1721546
commit 47f13abf7a
19 changed files with 241 additions and 260 deletions

View File

@ -435,7 +435,7 @@ func (hs *HTTPServer) registerRoutes() {
r.Any("/api/gnet/*", reqSignedIn, ProxyGnetRequest) r.Any("/api/gnet/*", reqSignedIn, ProxyGnetRequest)
// Gravatar service. // Gravatar service.
avatarCacheServer := avatar.NewCacheServer() avatarCacheServer := avatar.NewCacheServer(hs.Cfg)
r.Get("/avatar/:hash", avatarCacheServer.Handler) r.Get("/avatar/:hash", avatarCacheServer.Handler)
// Snapshots // Snapshots

View File

@ -69,6 +69,7 @@ func (a *Avatar) Update() (err error) {
} }
type CacheServer struct { type CacheServer struct {
cfg *setting.Cfg
notFound *Avatar notFound *Avatar
cache *gocache.Cache cache *gocache.Cache
} }
@ -109,7 +110,7 @@ func (a *CacheServer) Handler(ctx *models.ReqContext) {
ctx.Resp.Header().Set("Content-Type", "image/jpeg") ctx.Resp.Header().Set("Content-Type", "image/jpeg")
if !setting.EnableGzip { if !a.cfg.EnableGzip {
ctx.Resp.Header().Set("Content-Length", strconv.Itoa(len(avatar.data.Bytes()))) ctx.Resp.Header().Set("Content-Length", strconv.Itoa(len(avatar.data.Bytes())))
} }
@ -121,21 +122,22 @@ func (a *CacheServer) Handler(ctx *models.ReqContext) {
} }
} }
func NewCacheServer() *CacheServer { func NewCacheServer(cfg *setting.Cfg) *CacheServer {
return &CacheServer{ return &CacheServer{
notFound: newNotFound(), cfg: cfg,
notFound: newNotFound(cfg),
cache: gocache.New(time.Hour, time.Hour*2), cache: gocache.New(time.Hour, time.Hour*2),
} }
} }
func newNotFound() *Avatar { func newNotFound(cfg *setting.Cfg) *Avatar {
avatar := &Avatar{notFound: true} avatar := &Avatar{notFound: true}
// load user_profile png into buffer // load user_profile png into buffer
// It's safe to ignore gosec warning G304 since the variable part of the file path comes from a configuration // It's safe to ignore gosec warning G304 since the variable part of the file path comes from a configuration
// variable. // variable.
// nolint:gosec // nolint:gosec
path := filepath.Join(setting.StaticRootPath, "img", "user_profile.png") path := filepath.Join(cfg.StaticRootPath, "img", "user_profile.png")
// It's safe to ignore gosec warning G304 since the variable part of the file path comes from a configuration // It's safe to ignore gosec warning G304 since the variable part of the file path comes from a configuration
// variable. // variable.
// nolint:gosec // nolint:gosec

View File

@ -16,9 +16,10 @@ import (
) )
func TestHealthAPI_Version(t *testing.T) { func TestHealthAPI_Version(t *testing.T) {
m, _ := setupHealthAPITestEnvironment(t) m, _ := setupHealthAPITestEnvironment(t, func(cfg *setting.Cfg) {
setting.BuildVersion = "7.4.0" cfg.BuildVersion = "7.4.0"
setting.BuildCommit = "59906ab1bf" cfg.BuildCommit = "59906ab1bf"
})
bus.AddHandler("test", func(query *models.GetDBHealthQuery) error { bus.AddHandler("test", func(query *models.GetDBHealthQuery) error {
return nil return nil
@ -166,23 +167,20 @@ func TestHealthAPI_DatabaseHealthCached(t *testing.T) {
require.True(t, healthy.(bool)) require.True(t, healthy.(bool))
} }
func setupHealthAPITestEnvironment(t *testing.T) (*macaron.Macaron, *HTTPServer) { func setupHealthAPITestEnvironment(t *testing.T, cbs ...func(*setting.Cfg)) (*macaron.Macaron, *HTTPServer) {
t.Helper() t.Helper()
oldVersion := setting.BuildVersion
oldCommit := setting.BuildCommit
t.Cleanup(func() {
setting.BuildVersion = oldVersion
setting.BuildCommit = oldCommit
})
bus.ClearBusHandlers() bus.ClearBusHandlers()
t.Cleanup(bus.ClearBusHandlers) t.Cleanup(bus.ClearBusHandlers)
m := macaron.New() m := macaron.New()
cfg := setting.NewCfg()
for _, cb := range cbs {
cb(cfg)
}
hs := &HTTPServer{ hs := &HTTPServer{
CacheService: localcache.New(5*time.Minute, 10*time.Minute), CacheService: localcache.New(5*time.Minute, 10*time.Minute),
Cfg: setting.NewCfg(), Cfg: cfg,
} }
m.Get("/api/health", hs.apiHealthHandler) m.Get("/api/health", hs.apiHealthHandler)

View File

@ -111,9 +111,9 @@ func (hs *HTTPServer) Run(ctx context.Context) error {
hs.applyRoutes() hs.applyRoutes()
// Remove any square brackets enclosing IPv6 addresses, a format we support for backwards compatibility // Remove any square brackets enclosing IPv6 addresses, a format we support for backwards compatibility
host := strings.TrimSuffix(strings.TrimPrefix(setting.HttpAddr, "["), "]") host := strings.TrimSuffix(strings.TrimPrefix(hs.Cfg.HTTPAddr, "["), "]")
hs.httpSrv = &http.Server{ hs.httpSrv = &http.Server{
Addr: net.JoinHostPort(host, setting.HttpPort), Addr: net.JoinHostPort(host, hs.Cfg.HTTPPort),
Handler: hs.macaron, Handler: hs.macaron,
} }
switch hs.Cfg.Protocol { switch hs.Cfg.Protocol {
@ -159,7 +159,7 @@ func (hs *HTTPServer) Run(ctx context.Context) error {
return err return err
} }
case setting.HTTP2Scheme, setting.HTTPSScheme: case setting.HTTP2Scheme, setting.HTTPSScheme:
if err := hs.httpSrv.ServeTLS(listener, setting.CertFile, setting.KeyFile); err != nil { if err := hs.httpSrv.ServeTLS(listener, hs.Cfg.CertFile, hs.Cfg.KeyFile); err != nil {
if errors.Is(err, http.ErrServerClosed) { if errors.Is(err, http.ErrServerClosed) {
hs.log.Debug("server was shutdown gracefully") hs.log.Debug("server was shutdown gracefully")
return nil return nil
@ -207,20 +207,20 @@ func (hs *HTTPServer) getListener() (net.Listener, error) {
} }
func (hs *HTTPServer) configureHttps() error { func (hs *HTTPServer) configureHttps() error {
if setting.CertFile == "" { if hs.Cfg.CertFile == "" {
return fmt.Errorf("cert_file cannot be empty when using HTTPS") return fmt.Errorf("cert_file cannot be empty when using HTTPS")
} }
if setting.KeyFile == "" { if hs.Cfg.KeyFile == "" {
return fmt.Errorf("cert_key cannot be empty when using HTTPS") return fmt.Errorf("cert_key cannot be empty when using HTTPS")
} }
if _, err := os.Stat(setting.CertFile); os.IsNotExist(err) { if _, err := os.Stat(hs.Cfg.CertFile); os.IsNotExist(err) {
return fmt.Errorf(`cannot find SSL cert_file at %q`, setting.CertFile) return fmt.Errorf(`cannot find SSL cert_file at %q`, hs.Cfg.CertFile)
} }
if _, err := os.Stat(setting.KeyFile); os.IsNotExist(err) { if _, err := os.Stat(hs.Cfg.KeyFile); os.IsNotExist(err) {
return fmt.Errorf(`cannot find SSL key_file at %q`, setting.KeyFile) return fmt.Errorf(`cannot find SSL key_file at %q`, hs.Cfg.KeyFile)
} }
tlsCfg := &tls.Config{ tlsCfg := &tls.Config{
@ -248,20 +248,20 @@ func (hs *HTTPServer) configureHttps() error {
} }
func (hs *HTTPServer) configureHttp2() error { func (hs *HTTPServer) configureHttp2() error {
if setting.CertFile == "" { if hs.Cfg.CertFile == "" {
return fmt.Errorf("cert_file cannot be empty when using HTTP2") return fmt.Errorf("cert_file cannot be empty when using HTTP2")
} }
if setting.KeyFile == "" { if hs.Cfg.KeyFile == "" {
return fmt.Errorf("cert_key cannot be empty when using HTTP2") return fmt.Errorf("cert_key cannot be empty when using HTTP2")
} }
if _, err := os.Stat(setting.CertFile); os.IsNotExist(err) { if _, err := os.Stat(hs.Cfg.CertFile); os.IsNotExist(err) {
return fmt.Errorf(`cannot find SSL cert_file at %q`, setting.CertFile) return fmt.Errorf(`cannot find SSL cert_file at %q`, hs.Cfg.CertFile)
} }
if _, err := os.Stat(setting.KeyFile); os.IsNotExist(err) { if _, err := os.Stat(hs.Cfg.KeyFile); os.IsNotExist(err) {
return fmt.Errorf(`cannot find SSL key_file at %q`, setting.KeyFile) return fmt.Errorf(`cannot find SSL key_file at %q`, hs.Cfg.KeyFile)
} }
tlsCfg := &tls.Config{ tlsCfg := &tls.Config{
@ -312,7 +312,7 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
m.Use(middleware.Logger(hs.Cfg)) m.Use(middleware.Logger(hs.Cfg))
if setting.EnableGzip { if hs.Cfg.EnableGzip {
m.Use(middleware.Gziper()) m.Use(middleware.Gziper())
} }
@ -324,22 +324,22 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
hs.mapStatic(m, route.Directory, "", pluginRoute) hs.mapStatic(m, route.Directory, "", pluginRoute)
} }
hs.mapStatic(m, setting.StaticRootPath, "build", "public/build") hs.mapStatic(m, hs.Cfg.StaticRootPath, "build", "public/build")
hs.mapStatic(m, setting.StaticRootPath, "", "public") hs.mapStatic(m, hs.Cfg.StaticRootPath, "", "public")
hs.mapStatic(m, setting.StaticRootPath, "robots.txt", "robots.txt") hs.mapStatic(m, hs.Cfg.StaticRootPath, "robots.txt", "robots.txt")
if setting.ImageUploadProvider == "local" { if hs.Cfg.ImageUploadProvider == "local" {
hs.mapStatic(m, hs.Cfg.ImagesDir, "", "/public/img/attachments") hs.mapStatic(m, hs.Cfg.ImagesDir, "", "/public/img/attachments")
} }
m.Use(middleware.AddDefaultResponseHeaders(hs.Cfg)) m.Use(middleware.AddDefaultResponseHeaders(hs.Cfg))
if setting.ServeFromSubPath && setting.AppSubUrl != "" { if hs.Cfg.ServeFromSubPath && hs.Cfg.AppSubURL != "" {
m.SetURLPrefix(setting.AppSubUrl) m.SetURLPrefix(hs.Cfg.AppSubURL)
} }
m.Use(macaron.Renderer(macaron.RenderOptions{ m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: filepath.Join(setting.StaticRootPath, "views"), Directory: filepath.Join(hs.Cfg.StaticRootPath, "views"),
IndentJSON: macaron.Env != macaron.PROD, IndentJSON: macaron.Env != macaron.PROD,
Delims: macaron.Delims{Left: "[[", Right: "]]"}, Delims: macaron.Delims{Left: "[[", Right: "]]"},
})) }))
@ -354,7 +354,7 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
m.Use(middleware.OrgRedirect(hs.Cfg)) m.Use(middleware.OrgRedirect(hs.Cfg))
// needs to be after context handler // needs to be after context handler
if setting.EnforceDomain { if hs.Cfg.EnforceDomain {
m.Use(middleware.ValidateHostHeader(hs.Cfg)) m.Use(middleware.ValidateHostHeader(hs.Cfg))
} }
@ -411,8 +411,8 @@ func (hs *HTTPServer) apiHealthHandler(ctx *macaron.Context) {
data := simplejson.New() data := simplejson.New()
data.Set("database", "ok") data.Set("database", "ok")
if !hs.Cfg.AnonymousHideVersion { if !hs.Cfg.AnonymousHideVersion {
data.Set("version", setting.BuildVersion) data.Set("version", hs.Cfg.BuildVersion)
data.Set("commit", setting.BuildCommit) data.Set("commit", hs.Cfg.BuildCommit)
} }
if !hs.databaseHealthy() { if !hs.databaseHealthy() {

View File

@ -17,7 +17,7 @@ const (
darkName = "dark" darkName = "dark"
) )
func getProfileNode(c *models.ReqContext) *dtos.NavLink { func (hs *HTTPServer) getProfileNode(c *models.ReqContext) *dtos.NavLink {
// Only set login if it's different from the name // Only set login if it's different from the name
var login string var login string
if c.SignedInUser.Login != c.SignedInUser.NameOrFallback() { if c.SignedInUser.Login != c.SignedInUser.NameOrFallback() {
@ -27,13 +27,13 @@ func getProfileNode(c *models.ReqContext) *dtos.NavLink {
children := []*dtos.NavLink{ children := []*dtos.NavLink{
{ {
Text: "Preferences", Id: "profile-settings", Url: setting.AppSubUrl + "/profile", Icon: "sliders-v-alt", Text: "Preferences", Id: "profile-settings", Url: hs.Cfg.AppSubURL + "/profile", Icon: "sliders-v-alt",
}, },
} }
if setting.AddChangePasswordLink() { if setting.AddChangePasswordLink() {
children = append(children, &dtos.NavLink{ children = append(children, &dtos.NavLink{
Text: "Change Password", Id: "change-password", Url: setting.AppSubUrl + "/profile/password", Text: "Change Password", Id: "change-password", Url: hs.Cfg.AppSubURL + "/profile/password",
Icon: "lock", HideFromMenu: true, Icon: "lock", HideFromMenu: true,
}) })
} }
@ -43,7 +43,7 @@ func getProfileNode(c *models.ReqContext) *dtos.NavLink {
children = append(children, &dtos.NavLink{ children = append(children, &dtos.NavLink{
Text: "Sign out", Text: "Sign out",
Id: "sign-out", Id: "sign-out",
Url: setting.AppSubUrl + "/logout", Url: hs.Cfg.AppSubURL + "/logout",
Icon: "arrow-from-right", Icon: "arrow-from-right",
Target: "_self", Target: "_self",
HideFromTabs: true, HideFromTabs: true,
@ -55,7 +55,7 @@ func getProfileNode(c *models.ReqContext) *dtos.NavLink {
SubTitle: login, SubTitle: login,
Id: "profile", Id: "profile",
Img: gravatarURL, Img: gravatarURL,
Url: setting.AppSubUrl + "/profile", Url: hs.Cfg.AppSubURL + "/profile",
HideFromMenu: true, HideFromMenu: true,
SortWeight: dtos.WeightProfile, SortWeight: dtos.WeightProfile,
Children: children, Children: children,
@ -91,7 +91,7 @@ func (hs *HTTPServer) getAppLinks(c *models.ReqContext) ([]*dtos.NavLink, error)
var link *dtos.NavLink var link *dtos.NavLink
if len(include.Path) > 0 { if len(include.Path) > 0 {
link = &dtos.NavLink{ link = &dtos.NavLink{
Url: setting.AppSubUrl + include.Path, Url: hs.Cfg.AppSubURL + include.Path,
Text: include.Name, Text: include.Name,
} }
if include.DefaultNav { if include.DefaultNav {
@ -99,7 +99,7 @@ func (hs *HTTPServer) getAppLinks(c *models.ReqContext) ([]*dtos.NavLink, error)
} }
} else { } else {
link = &dtos.NavLink{ link = &dtos.NavLink{
Url: setting.AppSubUrl + "/plugins/" + plugin.Id + "/page/" + include.Slug, Url: hs.Cfg.AppSubURL + "/plugins/" + plugin.Id + "/page/" + include.Slug,
Text: include.Name, Text: include.Name,
} }
} }
@ -109,7 +109,7 @@ func (hs *HTTPServer) getAppLinks(c *models.ReqContext) ([]*dtos.NavLink, error)
if include.Type == "dashboard" && include.AddToNav { if include.Type == "dashboard" && include.AddToNav {
link := &dtos.NavLink{ link := &dtos.NavLink{
Url: setting.AppSubUrl + "/dashboard/db/" + include.Slug, Url: hs.Cfg.AppSubURL + "/dashboard/db/" + include.Slug,
Text: include.Name, Text: include.Name,
} }
appLink.Children = append(appLink.Children, link) appLink.Children = append(appLink.Children, link)
@ -129,40 +129,40 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
if hasEditPerm { if hasEditPerm {
children := []*dtos.NavLink{ children := []*dtos.NavLink{
{Text: "Dashboard", Icon: "apps", Url: setting.AppSubUrl + "/dashboard/new"}, {Text: "Dashboard", Icon: "apps", Url: hs.Cfg.AppSubURL + "/dashboard/new"},
} }
if c.OrgRole == models.ROLE_ADMIN || c.OrgRole == models.ROLE_EDITOR { if c.OrgRole == models.ROLE_ADMIN || c.OrgRole == models.ROLE_EDITOR {
children = append(children, &dtos.NavLink{ children = append(children, &dtos.NavLink{
Text: "Folder", SubTitle: "Create a new folder to organize your dashboards", Id: "folder", Text: "Folder", SubTitle: "Create a new folder to organize your dashboards", Id: "folder",
Icon: "folder-plus", Url: setting.AppSubUrl + "/dashboards/folder/new", Icon: "folder-plus", Url: hs.Cfg.AppSubURL + "/dashboards/folder/new",
}) })
} }
children = append(children, &dtos.NavLink{ children = append(children, &dtos.NavLink{
Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "import", Icon: "import", Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "import", Icon: "import",
Url: setting.AppSubUrl + "/dashboard/import", Url: hs.Cfg.AppSubURL + "/dashboard/import",
}) })
navTree = append(navTree, &dtos.NavLink{ navTree = append(navTree, &dtos.NavLink{
Text: "Create", Text: "Create",
Id: "create", Id: "create",
Icon: "plus", Icon: "plus",
Url: setting.AppSubUrl + "/dashboard/new", Url: hs.Cfg.AppSubURL + "/dashboard/new",
Children: children, Children: children,
SortWeight: dtos.WeightCreate, SortWeight: dtos.WeightCreate,
}) })
} }
dashboardChildNavs := []*dtos.NavLink{ dashboardChildNavs := []*dtos.NavLink{
{Text: "Home", Id: "home", Url: setting.AppSubUrl + "/", Icon: "home-alt", HideFromTabs: true}, {Text: "Home", Id: "home", Url: hs.Cfg.AppSubURL + "/", Icon: "home-alt", HideFromTabs: true},
{Text: "Divider", Divider: true, Id: "divider", HideFromTabs: true}, {Text: "Divider", Divider: true, Id: "divider", HideFromTabs: true},
{Text: "Manage", Id: "manage-dashboards", Url: setting.AppSubUrl + "/dashboards", Icon: "sitemap"}, {Text: "Manage", Id: "manage-dashboards", Url: hs.Cfg.AppSubURL + "/dashboards", Icon: "sitemap"},
{Text: "Playlists", Id: "playlists", Url: setting.AppSubUrl + "/playlists", Icon: "presentation-play"}, {Text: "Playlists", Id: "playlists", Url: hs.Cfg.AppSubURL + "/playlists", Icon: "presentation-play"},
} }
if c.IsSignedIn { if c.IsSignedIn {
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{ dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
Text: "Snapshots", Text: "Snapshots",
Id: "snapshots", Id: "snapshots",
Url: setting.AppSubUrl + "/dashboard/snapshots", Url: hs.Cfg.AppSubURL + "/dashboard/snapshots",
Icon: "camera", Icon: "camera",
}) })
} }
@ -172,7 +172,7 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
Id: "dashboards", Id: "dashboards",
SubTitle: "Manage dashboards & folders", SubTitle: "Manage dashboards & folders",
Icon: "apps", Icon: "apps",
Url: setting.AppSubUrl + "/", Url: hs.Cfg.AppSubURL + "/",
SortWeight: dtos.WeightDashboard, SortWeight: dtos.WeightDashboard,
Children: dashboardChildNavs, Children: dashboardChildNavs,
}) })
@ -184,19 +184,19 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
SubTitle: "Explore your data", SubTitle: "Explore your data",
Icon: "compass", Icon: "compass",
SortWeight: dtos.WeightExplore, SortWeight: dtos.WeightExplore,
Url: setting.AppSubUrl + "/explore", Url: hs.Cfg.AppSubURL + "/explore",
}) })
} }
if c.IsSignedIn { if c.IsSignedIn {
navTree = append(navTree, getProfileNode(c)) navTree = append(navTree, hs.getProfileNode(c))
} }
if setting.AlertingEnabled && (c.OrgRole == models.ROLE_ADMIN || c.OrgRole == models.ROLE_EDITOR) { if setting.AlertingEnabled && (c.OrgRole == models.ROLE_ADMIN || c.OrgRole == models.ROLE_EDITOR) {
alertChildNavs := []*dtos.NavLink{ alertChildNavs := []*dtos.NavLink{
{Text: "Alert Rules", Id: "alert-list", Url: setting.AppSubUrl + "/alerting/list", Icon: "list-ul"}, {Text: "Alert Rules", Id: "alert-list", Url: hs.Cfg.AppSubURL + "/alerting/list", Icon: "list-ul"},
{ {
Text: "Notification channels", Id: "channels", Url: setting.AppSubUrl + "/alerting/notifications", Text: "Notification channels", Id: "channels", Url: hs.Cfg.AppSubURL + "/alerting/notifications",
Icon: "comment-alt-share", Icon: "comment-alt-share",
}, },
} }
@ -206,7 +206,7 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
SubTitle: "Alert rules & notifications", SubTitle: "Alert rules & notifications",
Id: "alerting", Id: "alerting",
Icon: "bell", Icon: "bell",
Url: setting.AppSubUrl + "/alerting/list", Url: hs.Cfg.AppSubURL + "/alerting/list",
Children: alertChildNavs, Children: alertChildNavs,
SortWeight: dtos.WeightAlerting, SortWeight: dtos.WeightAlerting,
}) })
@ -226,14 +226,14 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
Icon: "database", Icon: "database",
Description: "Add and configure data sources", Description: "Add and configure data sources",
Id: "datasources", Id: "datasources",
Url: setting.AppSubUrl + "/datasources", Url: hs.Cfg.AppSubURL + "/datasources",
}) })
configNodes = append(configNodes, &dtos.NavLink{ configNodes = append(configNodes, &dtos.NavLink{
Text: "Users", Text: "Users",
Id: "users", Id: "users",
Description: "Manage org members", Description: "Manage org members",
Icon: "user", Icon: "user",
Url: setting.AppSubUrl + "/org/users", Url: hs.Cfg.AppSubURL + "/org/users",
}) })
} }
@ -243,7 +243,7 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
Id: "teams", Id: "teams",
Description: "Manage org groups", Description: "Manage org groups",
Icon: "users-alt", Icon: "users-alt",
Url: setting.AppSubUrl + "/org/teams", Url: hs.Cfg.AppSubURL + "/org/teams",
}) })
} }
@ -253,7 +253,7 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
Id: "plugins", Id: "plugins",
Description: "View and configure plugins", Description: "View and configure plugins",
Icon: "plug", Icon: "plug",
Url: setting.AppSubUrl + "/plugins", Url: hs.Cfg.AppSubURL + "/plugins",
}) })
configNodes = append(configNodes, &dtos.NavLink{ configNodes = append(configNodes, &dtos.NavLink{
@ -261,14 +261,14 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
Id: "org-settings", Id: "org-settings",
Description: "Organization preferences", Description: "Organization preferences",
Icon: "sliders-v-alt", Icon: "sliders-v-alt",
Url: setting.AppSubUrl + "/org", Url: hs.Cfg.AppSubURL + "/org",
}) })
configNodes = append(configNodes, &dtos.NavLink{ configNodes = append(configNodes, &dtos.NavLink{
Text: "API Keys", Text: "API Keys",
Id: "apikeys", Id: "apikeys",
Description: "Create & manage API keys", Description: "Create & manage API keys",
Icon: "key-skeleton-alt", Icon: "key-skeleton-alt",
Url: setting.AppSubUrl + "/org/apikeys", Url: hs.Cfg.AppSubURL + "/org/apikeys",
}) })
} }
@ -286,15 +286,15 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
if c.IsGrafanaAdmin { if c.IsGrafanaAdmin {
adminNavLinks := []*dtos.NavLink{ adminNavLinks := []*dtos.NavLink{
{Text: "Users", Id: "global-users", Url: setting.AppSubUrl + "/admin/users", Icon: "user"}, {Text: "Users", Id: "global-users", Url: hs.Cfg.AppSubURL + "/admin/users", Icon: "user"},
{Text: "Orgs", Id: "global-orgs", Url: setting.AppSubUrl + "/admin/orgs", Icon: "building"}, {Text: "Orgs", Id: "global-orgs", Url: hs.Cfg.AppSubURL + "/admin/orgs", Icon: "building"},
{Text: "Settings", Id: "server-settings", Url: setting.AppSubUrl + "/admin/settings", Icon: "sliders-v-alt"}, {Text: "Settings", Id: "server-settings", Url: hs.Cfg.AppSubURL + "/admin/settings", Icon: "sliders-v-alt"},
{Text: "Stats", Id: "server-stats", Url: setting.AppSubUrl + "/admin/stats", Icon: "graph-bar"}, {Text: "Stats", Id: "server-stats", Url: hs.Cfg.AppSubURL + "/admin/stats", Icon: "graph-bar"},
} }
if hs.Cfg.LDAPEnabled { if hs.Cfg.LDAPEnabled {
adminNavLinks = append(adminNavLinks, &dtos.NavLink{ adminNavLinks = append(adminNavLinks, &dtos.NavLink{
Text: "LDAP", Id: "ldap", Url: setting.AppSubUrl + "/admin/ldap", Icon: "book", Text: "LDAP", Id: "ldap", Url: hs.Cfg.AppSubURL + "/admin/ldap", Icon: "book",
}) })
} }
@ -304,7 +304,7 @@ func (hs *HTTPServer) getNavTree(c *models.ReqContext, hasEditPerm bool) ([]*dto
HideFromTabs: true, HideFromTabs: true,
Id: "admin", Id: "admin",
Icon: "shield", Icon: "shield",
Url: setting.AppSubUrl + "/admin/users", Url: hs.Cfg.AppSubURL + "/admin/users",
SortWeight: dtos.WeightAdmin, SortWeight: dtos.WeightAdmin,
Children: adminNavLinks, Children: adminNavLinks,
}) })
@ -359,11 +359,11 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
} }
appURL := setting.AppUrl appURL := setting.AppUrl
appSubURL := setting.AppSubUrl appSubURL := hs.Cfg.AppSubURL
// special case when doing localhost call from image renderer // special case when doing localhost call from image renderer
if c.IsRenderCall && !hs.Cfg.ServeFromSubPath { if c.IsRenderCall && !hs.Cfg.ServeFromSubPath {
appURL = fmt.Sprintf("%s://localhost:%s", hs.Cfg.Protocol, setting.HttpPort) appURL = fmt.Sprintf("%s://localhost:%s", hs.Cfg.Protocol, hs.Cfg.HTTPPort)
appSubURL = "" appSubURL = ""
settings["appSubUrl"] = "" settings["appSubUrl"] = ""
} }
@ -414,7 +414,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
} }
if setting.DisableGravatar { if setting.DisableGravatar {
data.User.GravatarUrl = setting.AppSubUrl + "/public/img/user_profile.png" data.User.GravatarUrl = hs.Cfg.AppSubURL + "/public/img/user_profile.png"
} }
if len(data.User.Name) == 0 { if len(data.User.Name) == 0 {

View File

@ -109,7 +109,7 @@ func (hs *HTTPServer) LoginView(c *models.ReqContext) {
return return
} }
if tryOAuthAutoLogin(c) { if hs.tryOAuthAutoLogin(c) {
return return
} }
@ -136,14 +136,14 @@ func (hs *HTTPServer) LoginView(c *models.ReqContext) {
return return
} }
c.Redirect(setting.AppSubUrl + "/") c.Redirect(hs.Cfg.AppSubURL + "/")
return return
} }
c.HTML(200, getViewIndex(), viewData) c.HTML(200, getViewIndex(), viewData)
} }
func tryOAuthAutoLogin(c *models.ReqContext) bool { func (hs *HTTPServer) tryOAuthAutoLogin(c *models.ReqContext) bool {
if !setting.OAuthAutoLogin { if !setting.OAuthAutoLogin {
return false return false
} }
@ -153,7 +153,7 @@ func tryOAuthAutoLogin(c *models.ReqContext) bool {
return false return false
} }
for key := range setting.OAuthService.OAuthInfos { for key := range setting.OAuthService.OAuthInfos {
redirectUrl := setting.AppSubUrl + "/login/" + key redirectUrl := hs.Cfg.AppSubURL + "/login/" + key
log.Infof("OAuth auto login enabled. Redirecting to " + redirectUrl) log.Infof("OAuth auto login enabled. Redirecting to " + redirectUrl)
c.Redirect(redirectUrl, 307) c.Redirect(redirectUrl, 307)
return true return true
@ -279,7 +279,7 @@ func (hs *HTTPServer) loginUserWithUser(user *models.User, c *models.ReqContext)
func (hs *HTTPServer) Logout(c *models.ReqContext) { func (hs *HTTPServer) Logout(c *models.ReqContext) {
if hs.Cfg.SAMLEnabled && hs.Cfg.SAMLSingleLogoutEnabled && hs.License.HasValidLicense() { if hs.Cfg.SAMLEnabled && hs.Cfg.SAMLSingleLogoutEnabled && hs.License.HasValidLicense() {
c.Redirect(setting.AppSubUrl + "/logout/saml") c.Redirect(hs.Cfg.AppSubURL + "/logout/saml")
return return
} }
@ -294,7 +294,7 @@ func (hs *HTTPServer) Logout(c *models.ReqContext) {
c.Redirect(setting.SignoutRedirectUrl) c.Redirect(setting.SignoutRedirectUrl)
} else { } else {
hs.log.Info("Successful Logout", "User", c.Email) hs.log.Info("Successful Logout", "User", c.Email)
c.Redirect(setting.AppSubUrl + "/login") c.Redirect(hs.Cfg.AppSubURL + "/login")
} }
} }
@ -330,7 +330,7 @@ func (hs *HTTPServer) redirectWithError(ctx *models.ReqContext, err error, v ...
hs.log.Error("Failed to set encrypted cookie", "err", err) hs.log.Error("Failed to set encrypted cookie", "err", err)
} }
ctx.Redirect(setting.AppSubUrl + "/login") ctx.Redirect(hs.Cfg.AppSubURL + "/login")
} }
func (hs *HTTPServer) RedirectResponseWithError(ctx *models.ReqContext, err error, v ...interface{}) *response.RedirectResponse { func (hs *HTTPServer) RedirectResponseWithError(ctx *models.ReqContext, err error, v ...interface{}) *response.RedirectResponse {
@ -339,7 +339,7 @@ func (hs *HTTPServer) RedirectResponseWithError(ctx *models.ReqContext, err erro
hs.log.Error("Failed to set encrypted cookie", "err", err) hs.log.Error("Failed to set encrypted cookie", "err", err)
} }
return response.Redirect(setting.AppSubUrl + "/login") return response.Redirect(hs.Cfg.AppSubURL + "/login")
} }
func getLoginExternalError(err error) string { func getLoginExternalError(err error) string {

View File

@ -17,7 +17,6 @@ import (
"github.com/grafana/grafana/pkg/plugins/adapters" "github.com/grafana/grafana/pkg/plugins/adapters"
"github.com/grafana/grafana/pkg/plugins/backendplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/manager" "github.com/grafana/grafana/pkg/plugins/manager"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil" "github.com/grafana/grafana/pkg/util/errutil"
) )
@ -122,7 +121,7 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) response.Response {
} }
if listItem.DefaultNavUrl == "" || !listItem.Enabled { if listItem.DefaultNavUrl == "" || !listItem.Enabled {
listItem.DefaultNavUrl = setting.AppSubUrl + "/plugins/" + listItem.Id + "/" listItem.DefaultNavUrl = hs.Cfg.AppSubURL + "/plugins/" + listItem.Id + "/"
} }
// filter out disabled plugins // filter out disabled plugins

View File

@ -213,7 +213,7 @@ func (hs *HTTPServer) ChangeActiveOrgAndRedirectToHome(c *models.ReqContext) {
hs.NotFoundHandler(c) hs.NotFoundHandler(c)
} }
c.Redirect(setting.AppSubUrl + "/") c.Redirect(hs.Cfg.AppSubURL + "/")
} }
func ChangeUserPassword(c *models.ReqContext, cmd models.ChangeUserPasswordCommand) response.Response { func ChangeUserPassword(c *models.ReqContext, cmd models.ChangeUserPasswordCommand) response.Response {

View File

@ -133,7 +133,7 @@ func Recovery(cfg *setting.Cfg) macaron.Handler {
} }
c.Data["Title"] = "Server Error" c.Data["Title"] = "Server Error"
c.Data["AppSubUrl"] = setting.AppSubUrl c.Data["AppSubUrl"] = cfg.AppSubURL
c.Data["Theme"] = cfg.DefaultTheme c.Data["Theme"] = cfg.DefaultTheme
if setting.Env == setting.Dev { if setting.Env == setting.Dev {

View File

@ -77,13 +77,14 @@ func (app *AppPlugin) Load(decoder *json.Decoder, base *PluginBase, backendPlugi
return app, nil return app, nil
} }
func (app *AppPlugin) InitApp(panels map[string]*PanelPlugin, dataSources map[string]*DataSourcePlugin) []*PluginStaticRoute { func (app *AppPlugin) InitApp(panels map[string]*PanelPlugin, dataSources map[string]*DataSourcePlugin,
staticRoutes := app.InitFrontendPlugin() cfg *setting.Cfg) []*PluginStaticRoute {
staticRoutes := app.InitFrontendPlugin(cfg)
// check if we have child panels // check if we have child panels
for _, panel := range panels { for _, panel := range panels {
if strings.HasPrefix(panel.PluginDir, app.PluginDir) { if strings.HasPrefix(panel.PluginDir, app.PluginDir) {
panel.setPathsBasedOnApp(app) panel.setPathsBasedOnApp(app, cfg)
app.FoundChildPlugins = append(app.FoundChildPlugins, &PluginInclude{ app.FoundChildPlugins = append(app.FoundChildPlugins, &PluginInclude{
Name: panel.Name, Name: panel.Name,
Id: panel.Id, Id: panel.Id,
@ -95,7 +96,7 @@ func (app *AppPlugin) InitApp(panels map[string]*PanelPlugin, dataSources map[st
// check if we have child datasources // check if we have child datasources
for _, ds := range dataSources { for _, ds := range dataSources {
if strings.HasPrefix(ds.PluginDir, app.PluginDir) { if strings.HasPrefix(ds.PluginDir, app.PluginDir) {
ds.setPathsBasedOnApp(app) ds.setPathsBasedOnApp(app, cfg)
app.FoundChildPlugins = append(app.FoundChildPlugins, &PluginInclude{ app.FoundChildPlugins = append(app.FoundChildPlugins, &PluginInclude{
Name: ds.Name, Name: ds.Name,
Id: ds.Id, Id: ds.Id,
@ -110,10 +111,10 @@ func (app *AppPlugin) InitApp(panels map[string]*PanelPlugin, dataSources map[st
include.Slug = slug.Make(include.Name) include.Slug = slug.Make(include.Name)
} }
if include.Type == "page" && include.DefaultNav { if include.Type == "page" && include.DefaultNav {
app.DefaultNavUrl = setting.AppSubUrl + "/plugins/" + app.Id + "/page/" + include.Slug app.DefaultNavUrl = cfg.AppSubURL + "/plugins/" + app.Id + "/page/" + include.Slug
} }
if include.Type == "dashboard" && include.DefaultNav { if include.Type == "dashboard" && include.DefaultNav {
app.DefaultNavUrl = setting.AppSubUrl + "/dashboard/db/" + include.Slug app.DefaultNavUrl = cfg.AppSubURL + "/dashboard/db/" + include.Slug
} }
} }

View File

@ -14,9 +14,9 @@ type FrontendPluginBase struct {
PluginBase PluginBase
} }
func (fp *FrontendPluginBase) InitFrontendPlugin() []*PluginStaticRoute { func (fp *FrontendPluginBase) InitFrontendPlugin(cfg *setting.Cfg) []*PluginStaticRoute {
var staticRoutes []*PluginStaticRoute var staticRoutes []*PluginStaticRoute
if isExternalPlugin(fp.PluginDir) { if isExternalPlugin(fp.PluginDir, cfg) {
staticRoutes = []*PluginStaticRoute{ staticRoutes = []*PluginStaticRoute{
{ {
Directory: fp.PluginDir, Directory: fp.PluginDir,
@ -25,7 +25,7 @@ func (fp *FrontendPluginBase) InitFrontendPlugin() []*PluginStaticRoute {
} }
} }
fp.handleModuleDefaults() fp.handleModuleDefaults(cfg)
fp.Info.Logos.Small = getPluginLogoUrl(fp.Type, fp.Info.Logos.Small, fp.BaseUrl) fp.Info.Logos.Small = getPluginLogoUrl(fp.Type, fp.Info.Logos.Small, fp.BaseUrl)
fp.Info.Logos.Large = getPluginLogoUrl(fp.Type, fp.Info.Logos.Large, fp.BaseUrl) fp.Info.Logos.Large = getPluginLogoUrl(fp.Type, fp.Info.Logos.Large, fp.BaseUrl)
@ -45,20 +45,20 @@ func getPluginLogoUrl(pluginType, path, baseUrl string) string {
return evalRelativePluginUrlPath(path, baseUrl) return evalRelativePluginUrlPath(path, baseUrl)
} }
func (fp *FrontendPluginBase) setPathsBasedOnApp(app *AppPlugin) { func (fp *FrontendPluginBase) setPathsBasedOnApp(app *AppPlugin, cfg *setting.Cfg) {
appSubPath := strings.ReplaceAll(strings.Replace(fp.PluginDir, app.PluginDir, "", 1), "\\", "/") appSubPath := strings.ReplaceAll(strings.Replace(fp.PluginDir, app.PluginDir, "", 1), "\\", "/")
fp.IncludedInAppId = app.Id fp.IncludedInAppId = app.Id
fp.BaseUrl = app.BaseUrl fp.BaseUrl = app.BaseUrl
if isExternalPlugin(app.PluginDir) { if isExternalPlugin(app.PluginDir, cfg) {
fp.Module = util.JoinURLFragments("plugins/"+app.Id, appSubPath) + "/module" fp.Module = util.JoinURLFragments("plugins/"+app.Id, appSubPath) + "/module"
} else { } else {
fp.Module = util.JoinURLFragments("app/plugins/app/"+app.Id, appSubPath) + "/module" fp.Module = util.JoinURLFragments("app/plugins/app/"+app.Id, appSubPath) + "/module"
} }
} }
func (fp *FrontendPluginBase) handleModuleDefaults() { func (fp *FrontendPluginBase) handleModuleDefaults(cfg *setting.Cfg) {
if isExternalPlugin(fp.PluginDir) { if isExternalPlugin(fp.PluginDir, cfg) {
fp.Module = path.Join("plugins", fp.Id, "module") fp.Module = path.Join("plugins", fp.Id, "module")
fp.BaseUrl = path.Join("public/plugins", fp.Id) fp.BaseUrl = path.Join("public/plugins", fp.Id)
return return
@ -75,8 +75,8 @@ func (fp *FrontendPluginBase) handleModuleDefaults() {
fp.BaseUrl = path.Join("public/app/plugins", fp.Type, currentDir) fp.BaseUrl = path.Join("public/app/plugins", fp.Type, currentDir)
} }
func isExternalPlugin(pluginDir string) bool { func isExternalPlugin(pluginDir string, cfg *setting.Cfg) bool {
return !strings.Contains(pluginDir, setting.StaticRootPath) return !strings.Contains(pluginDir, cfg.StaticRootPath)
} }
func evalRelativePluginUrlPath(pathStr string, baseUrl string) string { func evalRelativePluginUrlPath(pathStr string, baseUrl string) string {

View File

@ -26,7 +26,8 @@ func TestFrontendPlugin(t *testing.T) {
}, },
}, },
} }
fp.setPathsBasedOnApp(app) cfg := setting.NewCfg()
fp.setPathsBasedOnApp(app, cfg)
So(fp.Module, ShouldEqual, "app/plugins/app/testdata/datasources/datasource/module") So(fp.Module, ShouldEqual, "app/plugins/app/testdata/datasources/datasource/module")
}) })

View File

@ -130,22 +130,22 @@ func (pm *PluginManager) Init() error {
} }
for _, panel := range Panels { for _, panel := range Panels {
staticRoutes := panel.InitFrontendPlugin() staticRoutes := panel.InitFrontendPlugin(pm.Cfg)
StaticRoutes = append(StaticRoutes, staticRoutes...) StaticRoutes = append(StaticRoutes, staticRoutes...)
} }
for _, ds := range DataSources { for _, ds := range DataSources {
staticRoutes := ds.InitFrontendPlugin() staticRoutes := ds.InitFrontendPlugin(pm.Cfg)
StaticRoutes = append(StaticRoutes, staticRoutes...) StaticRoutes = append(StaticRoutes, staticRoutes...)
} }
for _, app := range Apps { for _, app := range Apps {
staticRoutes := app.InitApp(Panels, DataSources) staticRoutes := app.InitApp(Panels, DataSources, pm.Cfg)
StaticRoutes = append(StaticRoutes, staticRoutes...) StaticRoutes = append(StaticRoutes, staticRoutes...)
} }
if Renderer != nil { if Renderer != nil {
staticRoutes := Renderer.InitFrontendPlugin() staticRoutes := Renderer.InitFrontendPlugin(pm.Cfg)
StaticRoutes = append(StaticRoutes, staticRoutes...) StaticRoutes = append(StaticRoutes, staticRoutes...)
} }

View File

@ -18,34 +18,14 @@ import (
) )
func TestPluginManager_Init(t *testing.T) { func TestPluginManager_Init(t *testing.T) {
staticRootPath, err := filepath.Abs("../../../public/")
require.NoError(t, err)
origRootPath := setting.StaticRootPath
origRaw := setting.Raw
origEnv := setting.Env
t.Cleanup(func() {
setting.StaticRootPath = origRootPath
setting.Raw = origRaw
setting.Env = origEnv
})
setting.StaticRootPath = staticRootPath
setting.Raw = ini.Empty()
setting.Env = setting.Prod
t.Run("Base case", func(t *testing.T) { t.Run("Base case", func(t *testing.T) {
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginSettings = setting.PluginSettings{
Raw: ini.Empty(), "nginx-app": map[string]string{
Env: setting.Prod, "path": "testdata/test-app",
StaticRootPath: staticRootPath,
PluginSettings: setting.PluginSettings{
"nginx-app": map[string]string{
"path": "testdata/test-app",
},
}, },
}, }
} })
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
@ -59,9 +39,9 @@ func TestPluginManager_Init(t *testing.T) {
}) })
t.Run("With external back-end plugin lacking signature", func(t *testing.T) { t.Run("With external back-end plugin lacking signature", func(t *testing.T) {
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{PluginsPath: "testdata/unsigned"}, pm.Cfg.PluginsPath = "testdata/unsigned"
} })
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
@ -69,13 +49,10 @@ func TestPluginManager_Init(t *testing.T) {
}) })
t.Run("With external unsigned back-end plugin and configuration disabling signature check of this plugin", func(t *testing.T) { t.Run("With external unsigned back-end plugin and configuration disabling signature check of this plugin", func(t *testing.T) {
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginsPath = "testdata/unsigned"
PluginsPath: "testdata/unsigned", pm.Cfg.PluginsAllowUnsigned = []string{"test"}
PluginsAllowUnsigned: []string{"test"}, })
},
BackendPluginManager: &fakeBackendPluginManager{},
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
@ -83,11 +60,9 @@ func TestPluginManager_Init(t *testing.T) {
}) })
t.Run("With external back-end plugin with invalid v1 signature", func(t *testing.T) { t.Run("With external back-end plugin with invalid v1 signature", func(t *testing.T) {
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginsPath = "testdata/invalid-v1-signature"
PluginsPath: "testdata/invalid-v1-signature", })
},
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
@ -96,12 +71,10 @@ func TestPluginManager_Init(t *testing.T) {
t.Run("With external back-end plugin lacking files listed in manifest", func(t *testing.T) { t.Run("With external back-end plugin lacking files listed in manifest", func(t *testing.T) {
fm := &fakeBackendPluginManager{} fm := &fakeBackendPluginManager{}
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginsPath = "testdata/lacking-files"
PluginsPath: "testdata/lacking-files", pm.BackendPluginManager = fm
}, })
BackendPluginManager: fm,
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
@ -110,12 +83,10 @@ func TestPluginManager_Init(t *testing.T) {
t.Run("Transform plugins should be ignored when expressions feature is off", func(t *testing.T) { t.Run("Transform plugins should be ignored when expressions feature is off", func(t *testing.T) {
fm := fakeBackendPluginManager{} fm := fakeBackendPluginManager{}
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginsPath = "testdata/behind-feature-flag"
PluginsPath: "testdata/behind-feature-flag", pm.BackendPluginManager = &fm
}, })
BackendPluginManager: &fm,
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
@ -124,11 +95,9 @@ func TestPluginManager_Init(t *testing.T) {
}) })
t.Run("With nested plugin duplicating parent", func(t *testing.T) { t.Run("With nested plugin duplicating parent", func(t *testing.T) {
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginsPath = "testdata/duplicate-plugins"
PluginsPath: "testdata/duplicate-plugins", })
},
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
@ -137,26 +106,23 @@ func TestPluginManager_Init(t *testing.T) {
}) })
t.Run("With external back-end plugin with valid v2 signature", func(t *testing.T) { t.Run("With external back-end plugin with valid v2 signature", func(t *testing.T) {
pm := &PluginManager{ pm := createManager(t, func(manager *PluginManager) {
Cfg: &setting.Cfg{ manager.Cfg.PluginsPath = "testdata/valid-v2-signature"
PluginsPath: "testdata/valid-v2-signature", })
},
BackendPluginManager: &fakeBackendPluginManager{},
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, pm.scanningErrors) require.Empty(t, pm.scanningErrors)
pluginId := "test" const pluginID = "test"
assert.NotNil(t, Plugins[pluginId]) assert.NotNil(t, Plugins[pluginID])
assert.Equal(t, "datasource", Plugins[pluginId].Type) assert.Equal(t, "datasource", Plugins[pluginID].Type)
assert.Equal(t, "Test", Plugins[pluginId].Name) assert.Equal(t, "Test", Plugins[pluginID].Name)
assert.Equal(t, pluginId, Plugins[pluginId].Id) assert.Equal(t, pluginID, Plugins[pluginID].Id)
assert.Equal(t, "1.0.0", Plugins[pluginId].Info.Version) assert.Equal(t, "1.0.0", Plugins[pluginID].Info.Version)
assert.Equal(t, plugins.PluginSignatureValid, Plugins[pluginId].Signature) assert.Equal(t, plugins.PluginSignatureValid, Plugins[pluginID].Signature)
assert.Equal(t, plugins.GrafanaType, Plugins[pluginId].SignatureType) assert.Equal(t, plugins.GrafanaType, Plugins[pluginID].SignatureType)
assert.Equal(t, "Grafana Labs", Plugins[pluginId].SignatureOrg) assert.Equal(t, "Grafana Labs", Plugins[pluginID].SignatureOrg)
assert.False(t, Plugins[pluginId].IsCorePlugin) assert.False(t, Plugins[pluginID].IsCorePlugin)
}) })
t.Run("With back-end plugin with invalid v2 private signature (mismatched root URL)", func(t *testing.T) { t.Run("With back-end plugin with invalid v2 private signature (mismatched root URL)", func(t *testing.T) {
@ -166,11 +132,9 @@ func TestPluginManager_Init(t *testing.T) {
}) })
setting.AppUrl = "http://localhost:1234" setting.AppUrl = "http://localhost:1234"
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginsPath = "testdata/valid-v2-pvt-signature"
PluginsPath: "testdata/valid-v2-pvt-signature", })
},
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
@ -185,26 +149,23 @@ func TestPluginManager_Init(t *testing.T) {
}) })
setting.AppUrl = "http://localhost:3000/" setting.AppUrl = "http://localhost:3000/"
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginsPath = "testdata/valid-v2-pvt-signature"
PluginsPath: "testdata/valid-v2-pvt-signature", })
},
BackendPluginManager: &fakeBackendPluginManager{},
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
require.Empty(t, pm.scanningErrors) require.Empty(t, pm.scanningErrors)
pluginId := "test" const pluginID = "test"
assert.NotNil(t, Plugins[pluginId]) assert.NotNil(t, Plugins[pluginID])
assert.Equal(t, "datasource", Plugins[pluginId].Type) assert.Equal(t, "datasource", Plugins[pluginID].Type)
assert.Equal(t, "Test", Plugins[pluginId].Name) assert.Equal(t, "Test", Plugins[pluginID].Name)
assert.Equal(t, pluginId, Plugins[pluginId].Id) assert.Equal(t, pluginID, Plugins[pluginID].Id)
assert.Equal(t, "1.0.0", Plugins[pluginId].Info.Version) assert.Equal(t, "1.0.0", Plugins[pluginID].Info.Version)
assert.Equal(t, plugins.PluginSignatureValid, Plugins[pluginId].Signature) assert.Equal(t, plugins.PluginSignatureValid, Plugins[pluginID].Signature)
assert.Equal(t, plugins.PrivateType, Plugins[pluginId].SignatureType) assert.Equal(t, plugins.PrivateType, Plugins[pluginID].SignatureType)
assert.Equal(t, "Will Browne", Plugins[pluginId].SignatureOrg) assert.Equal(t, "Will Browne", Plugins[pluginID].SignatureOrg)
assert.False(t, Plugins[pluginId].IsCorePlugin) assert.False(t, Plugins[pluginID].IsCorePlugin)
}) })
t.Run("With back-end plugin with modified v2 signature (missing file from plugin dir)", func(t *testing.T) { t.Run("With back-end plugin with modified v2 signature (missing file from plugin dir)", func(t *testing.T) {
@ -214,12 +175,9 @@ func TestPluginManager_Init(t *testing.T) {
}) })
setting.AppUrl = "http://localhost:3000/" setting.AppUrl = "http://localhost:3000/"
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginsPath = "testdata/invalid-v2-signature"
PluginsPath: "testdata/invalid-v2-signature", })
},
BackendPluginManager: &fakeBackendPluginManager{},
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors) assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors)
@ -233,12 +191,9 @@ func TestPluginManager_Init(t *testing.T) {
}) })
setting.AppUrl = "http://localhost:3000/" setting.AppUrl = "http://localhost:3000/"
pm := &PluginManager{ pm := createManager(t, func(pm *PluginManager) {
Cfg: &setting.Cfg{ pm.Cfg.PluginsPath = "testdata/invalid-v2-signature-2"
PluginsPath: "testdata/invalid-v2-signature-2", })
},
BackendPluginManager: &fakeBackendPluginManager{},
}
err := pm.Init() err := pm.Init()
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors) assert.Equal(t, []error{fmt.Errorf(`plugin "test"'s signature has been modified`)}, pm.scanningErrors)
@ -291,3 +246,24 @@ func (f *fakeBackendPluginManager) CheckHealth(ctx context.Context, pCtx backend
func (f *fakeBackendPluginManager) CallResource(pluginConfig backend.PluginContext, ctx *models.ReqContext, path string) { func (f *fakeBackendPluginManager) CallResource(pluginConfig backend.PluginContext, ctx *models.ReqContext, path string) {
} }
func createManager(t *testing.T, cbs ...func(*PluginManager)) *PluginManager {
t.Helper()
staticRootPath, err := filepath.Abs("../../../public/")
require.NoError(t, err)
pm := &PluginManager{
Cfg: &setting.Cfg{
Raw: ini.Empty(),
Env: setting.Prod,
StaticRootPath: staticRootPath,
},
BackendPluginManager: &fakeBackendPluginManager{},
}
for _, cb := range cbs {
cb(pm)
}
return pm
}

View File

@ -56,7 +56,7 @@ func (ns *NotificationService) Init() error {
"Subject": subjectTemplateFunc, "Subject": subjectTemplateFunc,
}) })
templatePattern := filepath.Join(setting.StaticRootPath, ns.Cfg.Smtp.TemplatesPattern) templatePattern := filepath.Join(ns.Cfg.StaticRootPath, ns.Cfg.Smtp.TemplatesPattern)
_, err := mailTemplates.ParseGlob(templatePattern) _, err := mailTemplates.ParseGlob(templatePattern)
if err != nil { if err != nil {
return err return err

View File

@ -6,32 +6,31 @@ import (
"github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestNotifications(t *testing.T) { func TestNotificationService(t *testing.T) {
Convey("Given the notifications service", t, func() { ns := &NotificationService{
setting.StaticRootPath = "../../../public/" Cfg: setting.NewCfg(),
}
ns.Cfg.StaticRootPath = "../../../public/"
ns.Cfg.Smtp.Enabled = true
ns.Cfg.Smtp.TemplatesPattern = "emails/*.html"
ns.Cfg.Smtp.FromAddress = "from@address.com"
ns.Cfg.Smtp.FromName = "Grafana Admin"
ns.Bus = bus.New()
ns := &NotificationService{} err := ns.Init()
ns.Bus = bus.New() require.NoError(t, err)
ns.Cfg = setting.NewCfg()
ns.Cfg.Smtp.Enabled = true
ns.Cfg.Smtp.TemplatesPattern = "emails/*.html"
ns.Cfg.Smtp.FromAddress = "from@address.com"
ns.Cfg.Smtp.FromName = "Grafana Admin"
err := ns.Init() t.Run("When sending reset email password", func(t *testing.T) {
So(err, ShouldBeNil) err := ns.sendResetPasswordEmail(&models.SendResetPasswordEmailCommand{User: &models.User{Email: "asd@asd.com"}})
require.NoError(t, err)
Convey("When sending reset email password", func() { sentMsg := <-ns.mailQueue
err := ns.sendResetPasswordEmail(&models.SendResetPasswordEmailCommand{User: &models.User{Email: "asd@asd.com"}}) assert.Contains(t, sentMsg.Body, "body")
So(err, ShouldBeNil) assert.Equal(t, "Reset your Grafana password - asd@asd.com", sentMsg.Subject)
assert.NotContains(t, sentMsg.Body, "Subject")
sentMsg := <-ns.mailQueue
So(sentMsg.Body, ShouldContainSubstring, "body")
So(sentMsg.Subject, ShouldEqual, "Reset your Grafana password - asd@asd.com")
So(sentMsg.Body, ShouldNotContainSubstring, "Subject")
})
}) })
} }

View File

@ -67,8 +67,8 @@ func (rs *RenderingService) Init() error {
// RendererCallbackUrl has already been passed, it won't generate an error. // RendererCallbackUrl has already been passed, it won't generate an error.
u, _ := url.Parse(rs.Cfg.RendererCallbackUrl) u, _ := url.Parse(rs.Cfg.RendererCallbackUrl)
rs.domain = u.Hostname() rs.domain = u.Hostname()
case setting.HttpAddr != setting.DefaultHTTPAddr: case rs.Cfg.HTTPAddr != setting.DefaultHTTPAddr:
rs.domain = setting.HttpAddr rs.domain = rs.Cfg.HTTPAddr
default: default:
rs.domain = "localhost" rs.domain = "localhost"
} }
@ -244,7 +244,7 @@ func (rs *RenderingService) getURL(path string) string {
} }
// &render=1 signals to the legacy redirect layer to // &render=1 signals to the legacy redirect layer to
return fmt.Sprintf("%s://%s:%s%s/%s&render=1", protocol, rs.domain, setting.HttpPort, subPath, path) return fmt.Sprintf("%s://%s:%s%s/%s&render=1", protocol, rs.domain, rs.Cfg.HTTPPort, subPath, path)
} }
func (rs *RenderingService) generateAndStoreRenderKey(orgId, userId int64, orgRole models.RoleType) (string, error) { func (rs *RenderingService) generateAndStoreRenderKey(orgId, userId int64, orgRole models.RoleType) (string, error) {

View File

@ -23,7 +23,7 @@ func TestGetUrl(t *testing.T) {
t.Run("When renderer url not configured", func(t *testing.T) { t.Run("When renderer url not configured", func(t *testing.T) {
rs.Cfg.RendererUrl = "" rs.Cfg.RendererUrl = ""
rs.domain = "localhost" rs.domain = "localhost"
setting.HttpPort = "3000" rs.Cfg.HTTPPort = "3000"
t.Run("And protocol HTTP configured should return expected path", func(t *testing.T) { t.Run("And protocol HTTP configured should return expected path", func(t *testing.T) {
rs.Cfg.ServeFromSubPath = false rs.Cfg.ServeFromSubPath = false

View File

@ -70,8 +70,6 @@ var (
CustomInitPath = "conf/custom.ini" CustomInitPath = "conf/custom.ini"
// HTTP server options // HTTP server options
HttpAddr, HttpPort string
CertFile, KeyFile string
DataProxyLogging bool DataProxyLogging bool
DataProxyTimeout int DataProxyTimeout int
DataProxyTLSHandshakeTimeout int DataProxyTLSHandshakeTimeout int
@ -80,8 +78,6 @@ var (
DataProxyKeepAlive int DataProxyKeepAlive int
DataProxyIdleConnTimeout int DataProxyIdleConnTimeout int
StaticRootPath string StaticRootPath string
EnableGzip bool
EnforceDomain bool
// Security settings. // Security settings.
SecretKey string SecretKey string
@ -187,6 +183,10 @@ type Cfg struct {
Logger log.Logger Logger log.Logger
// HTTP Server Settings // HTTP Server Settings
CertFile string
KeyFile string
HTTPAddr string
HTTPPort string
AppURL string AppURL string
AppSubURL string AppSubURL string
ServeFromSubPath bool ServeFromSubPath bool
@ -196,6 +196,8 @@ type Cfg struct {
RouterLogging bool RouterLogging bool
Domain string Domain string
CDNRootURL *url.URL CDNRootURL *url.URL
EnableGzip bool
EnforceDomain bool
// build // build
BuildVersion string BuildVersion string
@ -353,6 +355,8 @@ type Cfg struct {
// ExpressionsEnabled specifies whether expressions are enabled. // ExpressionsEnabled specifies whether expressions are enabled.
ExpressionsEnabled bool ExpressionsEnabled bool
ImageUploadProvider string
} }
// IsLiveEnabled returns if grafana live should be enabled // IsLiveEnabled returns if grafana live should be enabled
@ -895,7 +899,8 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
} }
imageUploadingSection := iniFile.Section("external_image_storage") imageUploadingSection := iniFile.Section("external_image_storage")
ImageUploadProvider = valueAsString(imageUploadingSection, "provider", "") cfg.ImageUploadProvider = valueAsString(imageUploadingSection, "provider", "")
ImageUploadProvider = cfg.ImageUploadProvider
enterprise := iniFile.Section("enterprise") enterprise := iniFile.Section("enterprise")
cfg.EnterpriseLicensePath = valueAsString(enterprise, "license_path", filepath.Join(cfg.DataPath, "license.jwt")) cfg.EnterpriseLicensePath = valueAsString(enterprise, "license_path", filepath.Join(cfg.DataPath, "license.jwt"))
@ -1303,13 +1308,13 @@ func (cfg *Cfg) readServerSettings(iniFile *ini.File) error {
if protocolStr == "https" { if protocolStr == "https" {
cfg.Protocol = HTTPSScheme cfg.Protocol = HTTPSScheme
CertFile = server.Key("cert_file").String() cfg.CertFile = server.Key("cert_file").String()
KeyFile = server.Key("cert_key").String() cfg.KeyFile = server.Key("cert_key").String()
} }
if protocolStr == "h2" { if protocolStr == "h2" {
cfg.Protocol = HTTP2Scheme cfg.Protocol = HTTP2Scheme
CertFile = server.Key("cert_file").String() cfg.CertFile = server.Key("cert_file").String()
KeyFile = server.Key("cert_key").String() cfg.KeyFile = server.Key("cert_key").String()
} }
if protocolStr == "socket" { if protocolStr == "socket" {
cfg.Protocol = SocketScheme cfg.Protocol = SocketScheme
@ -1317,12 +1322,12 @@ func (cfg *Cfg) readServerSettings(iniFile *ini.File) error {
} }
cfg.Domain = valueAsString(server, "domain", "localhost") cfg.Domain = valueAsString(server, "domain", "localhost")
HttpAddr = valueAsString(server, "http_addr", DefaultHTTPAddr) cfg.HTTPAddr = valueAsString(server, "http_addr", DefaultHTTPAddr)
HttpPort = valueAsString(server, "http_port", "3000") cfg.HTTPPort = valueAsString(server, "http_port", "3000")
cfg.RouterLogging = server.Key("router_logging").MustBool(false) cfg.RouterLogging = server.Key("router_logging").MustBool(false)
EnableGzip = server.Key("enable_gzip").MustBool(false) cfg.EnableGzip = server.Key("enable_gzip").MustBool(false)
EnforceDomain = server.Key("enforce_domain").MustBool(false) cfg.EnforceDomain = server.Key("enforce_domain").MustBool(false)
staticRoot := valueAsString(server, "static_root_path", "") staticRoot := valueAsString(server, "static_root_path", "")
StaticRootPath = makeAbsolute(staticRoot, HomePath) StaticRootPath = makeAbsolute(staticRoot, HomePath)
cfg.StaticRootPath = StaticRootPath cfg.StaticRootPath = StaticRootPath