mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 18:52:38 +08:00
PluginManager: Remove some global state (#31081)
* PluginManager: Remove global state Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
@ -224,8 +224,8 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
|
||||
"commit": commit,
|
||||
"buildstamp": buildstamp,
|
||||
"edition": hs.License.Edition(),
|
||||
"latestVersion": plugins.GrafanaLatestVersion,
|
||||
"hasUpdate": plugins.GrafanaHasUpdate,
|
||||
"latestVersion": hs.PluginManager.GrafanaLatestVersion,
|
||||
"hasUpdate": hs.PluginManager.GrafanaHasUpdate,
|
||||
"env": setting.Env,
|
||||
"isEnterprise": hs.License.HasValidLicense(),
|
||||
},
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
@ -47,8 +48,9 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg) (*macaron.Macaron, *HT
|
||||
hs := &HTTPServer{
|
||||
Cfg: cfg,
|
||||
Bus: bus.GetBus(),
|
||||
License: &licensing.OSSLicensingService{},
|
||||
License: &licensing.OSSLicensingService{Cfg: cfg},
|
||||
RenderService: r,
|
||||
PluginManager: &plugins.PluginManager{Cfg: cfg},
|
||||
}
|
||||
|
||||
m := macaron.New()
|
||||
@ -74,13 +76,17 @@ func TestHTTPServer_GetFrontendSettings_hideVersionAnonyomus(t *testing.T) {
|
||||
}
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Env = "testing"
|
||||
cfg.BuildVersion = "7.8.9"
|
||||
cfg.BuildCommit = "01234567"
|
||||
m, hs := setupTestEnvironment(t, cfg)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/frontend/settings", nil)
|
||||
|
||||
setting.BuildVersion = "7.8.9"
|
||||
setting.BuildCommit = "01234567"
|
||||
setting.Env = "testing"
|
||||
// TODO: Remove
|
||||
setting.BuildVersion = cfg.BuildVersion
|
||||
setting.BuildCommit = cfg.BuildCommit
|
||||
setting.Env = cfg.Env
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
|
@ -408,8 +408,8 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
GoogleTagManagerId: setting.GoogleTagManagerId,
|
||||
BuildVersion: setting.BuildVersion,
|
||||
BuildCommit: setting.BuildCommit,
|
||||
NewGrafanaVersion: plugins.GrafanaLatestVersion,
|
||||
NewGrafanaVersionExists: plugins.GrafanaHasUpdate,
|
||||
NewGrafanaVersion: hs.PluginManager.GrafanaLatestVersion,
|
||||
NewGrafanaVersionExists: hs.PluginManager.GrafanaHasUpdate,
|
||||
AppName: setting.ApplicationName,
|
||||
AppNameBodyClass: getAppNameBodyClass(hs.License.HasValidLicense()),
|
||||
FavIcon: "public/img/fav32.png",
|
||||
|
@ -368,7 +368,7 @@ func (hs *HTTPServer) getCachedPluginSettings(pluginID string, user *models.Sign
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) GetPluginErrorsList(c *models.ReqContext) response.Response {
|
||||
return response.JSON(200, plugins.ScanningErrors())
|
||||
return response.JSON(200, hs.PluginManager.ScanningErrors())
|
||||
}
|
||||
|
||||
func translatePluginRequestErrorToAPIError(err error) response.Response {
|
||||
|
@ -32,11 +32,7 @@ var (
|
||||
PluginTypes map[string]interface{}
|
||||
Renderer *RendererPlugin
|
||||
|
||||
GrafanaLatestVersion string
|
||||
GrafanaHasUpdate bool
|
||||
plog log.Logger
|
||||
|
||||
pluginScanningErrors map[string]*PluginError
|
||||
)
|
||||
|
||||
type unsignedPluginConditionFunc = func(plugin *PluginBase) bool
|
||||
@ -61,6 +57,9 @@ type PluginManager struct {
|
||||
// AllowUnsignedPluginsCondition changes the policy for allowing unsigned plugins. Signature validation only runs when plugins are starting
|
||||
// and running plugins will not be terminated if they violate the new policy.
|
||||
AllowUnsignedPluginsCondition unsignedPluginConditionFunc
|
||||
GrafanaLatestVersion string
|
||||
GrafanaHasUpdate bool
|
||||
pluginScanningErrors map[string]PluginError
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -82,11 +81,11 @@ func (pm *PluginManager) Init() error {
|
||||
"app": AppPlugin{},
|
||||
"renderer": RendererPlugin{},
|
||||
}
|
||||
pluginScanningErrors = map[string]*PluginError{}
|
||||
pm.pluginScanningErrors = map[string]PluginError{}
|
||||
|
||||
pm.log.Info("Starting plugin search")
|
||||
|
||||
plugDir := filepath.Join(setting.StaticRootPath, "app/plugins")
|
||||
plugDir := filepath.Join(pm.Cfg.StaticRootPath, "app/plugins")
|
||||
pm.log.Debug("Scanning core plugin directory", "dir", plugDir)
|
||||
if err := pm.scan(plugDir, false); err != nil {
|
||||
return errutil.Wrapf(err, "failed to scan core plugin directory '%s'", plugDir)
|
||||
@ -105,21 +104,21 @@ func (pm *PluginManager) Init() error {
|
||||
}
|
||||
|
||||
// check if plugins dir exists
|
||||
exists, err = fs.Exists(setting.PluginsPath)
|
||||
exists, err = fs.Exists(pm.Cfg.PluginsPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
if err = os.MkdirAll(setting.PluginsPath, os.ModePerm); err != nil {
|
||||
pm.log.Error("failed to create external plugins directory", "dir", setting.PluginsPath, "error", err)
|
||||
if err = os.MkdirAll(pm.Cfg.PluginsPath, os.ModePerm); err != nil {
|
||||
pm.log.Error("failed to create external plugins directory", "dir", pm.Cfg.PluginsPath, "error", err)
|
||||
} else {
|
||||
pm.log.Info("External plugins directory created", "directory", setting.PluginsPath)
|
||||
pm.log.Info("External plugins directory created", "directory", pm.Cfg.PluginsPath)
|
||||
}
|
||||
} else {
|
||||
pm.log.Debug("Scanning external plugins directory", "dir", setting.PluginsPath)
|
||||
if err := pm.scan(setting.PluginsPath, true); err != nil {
|
||||
pm.log.Debug("Scanning external plugins directory", "dir", pm.Cfg.PluginsPath)
|
||||
if err := pm.scan(pm.Cfg.PluginsPath, true); err != nil {
|
||||
return errutil.Wrapf(err, "failed to scan external plugins directory '%s'",
|
||||
setting.PluginsPath)
|
||||
pm.Cfg.PluginsPath)
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,7 +240,7 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
|
||||
if signingError != nil {
|
||||
pm.log.Debug("Failed to validate plugin signature. Will skip loading", "id", plugin.Id,
|
||||
"signature", plugin.Signature, "status", signingError.ErrorCode)
|
||||
pluginScanningErrors[plugin.Id] = signingError
|
||||
pm.pluginScanningErrors[plugin.Id] = *signingError
|
||||
continue
|
||||
}
|
||||
|
||||
@ -255,7 +254,7 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
|
||||
jsonFPath := filepath.Join(plugin.PluginDir, "plugin.json")
|
||||
|
||||
// External plugins need a module.js file for SystemJS to load
|
||||
if !strings.HasPrefix(jsonFPath, setting.StaticRootPath) && !scanner.IsBackendOnlyPlugin(plugin.Type) {
|
||||
if !strings.HasPrefix(jsonFPath, pm.Cfg.StaticRootPath) && !scanner.IsBackendOnlyPlugin(plugin.Type) {
|
||||
module := filepath.Join(plugin.PluginDir, "module.js")
|
||||
exists, err := fs.Exists(module)
|
||||
if err != nil {
|
||||
@ -460,7 +459,7 @@ func (s *PluginScanner) allowUnsigned(plugin *PluginBase) bool {
|
||||
return s.allowUnsignedPluginsCondition(plugin)
|
||||
}
|
||||
|
||||
if setting.Env == setting.Dev {
|
||||
if s.cfg.Env == setting.Dev {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -473,9 +472,10 @@ func (s *PluginScanner) allowUnsigned(plugin *PluginBase) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func ScanningErrors() []PluginError {
|
||||
// ScanningErrors returns plugin scanning errors encountered.
|
||||
func (pm *PluginManager) ScanningErrors() []PluginError {
|
||||
scanningErrs := make([]PluginError, 0)
|
||||
for id, e := range pluginScanningErrors {
|
||||
for id, e := range pm.pluginScanningErrors {
|
||||
scanningErrs = append(scanningErrs, PluginError{
|
||||
ErrorCode: e.ErrorCode,
|
||||
PluginID: id,
|
||||
|
@ -17,6 +17,9 @@ import (
|
||||
)
|
||||
|
||||
func TestPluginManager_Init(t *testing.T) {
|
||||
staticRootPath, err := filepath.Abs("../../public/")
|
||||
require.NoError(t, err)
|
||||
|
||||
origRootPath := setting.StaticRootPath
|
||||
origRaw := setting.Raw
|
||||
origEnv := setting.Env
|
||||
@ -25,16 +28,16 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
setting.Raw = origRaw
|
||||
setting.Env = origEnv
|
||||
})
|
||||
|
||||
var err error
|
||||
setting.StaticRootPath, err = filepath.Abs("../../public/")
|
||||
require.NoError(t, err)
|
||||
setting.StaticRootPath = staticRootPath
|
||||
setting.Raw = ini.Empty()
|
||||
setting.Env = setting.Prod
|
||||
|
||||
t.Run("Base case", func(t *testing.T) {
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{
|
||||
Raw: ini.Empty(),
|
||||
Env: setting.Prod,
|
||||
StaticRootPath: staticRootPath,
|
||||
PluginSettings: setting.PluginSettings{
|
||||
"nginx-app": map[string]string{
|
||||
"path": "testdata/test-app",
|
||||
@ -55,14 +58,8 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("With external back-end plugin lacking signature", func(t *testing.T) {
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.PluginsPath = "testdata/unsigned"
|
||||
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{PluginsPath: "testdata/unsigned"},
|
||||
}
|
||||
err := pm.Init()
|
||||
require.NoError(t, err)
|
||||
@ -71,14 +68,9 @@ 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) {
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.PluginsPath = "testdata/unsigned"
|
||||
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/unsigned",
|
||||
PluginsAllowUnsigned: []string{"test"},
|
||||
},
|
||||
BackendPluginManager: &fakeBackendPluginManager{},
|
||||
@ -90,14 +82,10 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("With external back-end plugin with invalid v1 signature", func(t *testing.T) {
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.PluginsPath = "testdata/invalid-v1-signature"
|
||||
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/invalid-v1-signature",
|
||||
},
|
||||
}
|
||||
err := pm.Init()
|
||||
require.NoError(t, err)
|
||||
@ -106,15 +94,11 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("With external back-end plugin lacking files listed in manifest", func(t *testing.T) {
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.PluginsPath = "testdata/lacking-files"
|
||||
|
||||
fm := &fakeBackendPluginManager{}
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/lacking-files",
|
||||
},
|
||||
BackendPluginManager: fm,
|
||||
}
|
||||
err := pm.Init()
|
||||
@ -124,15 +108,11 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Transform plugins should be ignored when expressions feature is off", func(t *testing.T) {
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.PluginsPath = "testdata/behind-feature-flag"
|
||||
|
||||
fm := fakeBackendPluginManager{}
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/behind-feature-flag",
|
||||
},
|
||||
BackendPluginManager: &fm,
|
||||
}
|
||||
err := pm.Init()
|
||||
@ -143,14 +123,10 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("With nested plugin duplicating parent", func(t *testing.T) {
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.PluginsPath = "testdata/duplicate-plugins"
|
||||
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/duplicate-plugins",
|
||||
},
|
||||
}
|
||||
err := pm.Init()
|
||||
require.NoError(t, err)
|
||||
@ -160,14 +136,10 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("With external back-end plugin with valid v2 signature", func(t *testing.T) {
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.PluginsPath = "testdata/valid-v2-signature"
|
||||
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/valid-v2-signature",
|
||||
},
|
||||
BackendPluginManager: &fakeBackendPluginManager{},
|
||||
}
|
||||
err := pm.Init()
|
||||
@ -188,16 +160,15 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
|
||||
t.Run("With back-end plugin with invalid v2 private signature (mismatched root URL)", func(t *testing.T) {
|
||||
origAppURL := setting.AppUrl
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.AppUrl = origAppURL
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.AppUrl = "http://localhost:1234"
|
||||
setting.PluginsPath = "testdata/valid-v2-pvt-signature"
|
||||
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/valid-v2-pvt-signature",
|
||||
},
|
||||
}
|
||||
err := pm.Init()
|
||||
require.NoError(t, err)
|
||||
@ -208,16 +179,15 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
|
||||
t.Run("With back-end plugin with valid v2 private signature", func(t *testing.T) {
|
||||
origAppURL := setting.AppUrl
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.AppUrl = origAppURL
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.AppUrl = "http://localhost:3000/"
|
||||
setting.PluginsPath = "testdata/valid-v2-pvt-signature"
|
||||
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/valid-v2-pvt-signature",
|
||||
},
|
||||
BackendPluginManager: &fakeBackendPluginManager{},
|
||||
}
|
||||
err := pm.Init()
|
||||
@ -238,16 +208,15 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
|
||||
t.Run("With back-end plugin with modified v2 signature (missing file from plugin dir)", func(t *testing.T) {
|
||||
origAppURL := setting.AppUrl
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.AppUrl = origAppURL
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.AppUrl = "http://localhost:3000/"
|
||||
setting.PluginsPath = "testdata/invalid-v2-signature"
|
||||
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/invalid-v2-signature",
|
||||
},
|
||||
BackendPluginManager: &fakeBackendPluginManager{},
|
||||
}
|
||||
err := pm.Init()
|
||||
@ -258,16 +227,15 @@ func TestPluginManager_Init(t *testing.T) {
|
||||
|
||||
t.Run("With back-end plugin with modified v2 signature (unaccounted file in plugin dir)", func(t *testing.T) {
|
||||
origAppURL := setting.AppUrl
|
||||
origPluginsPath := setting.PluginsPath
|
||||
t.Cleanup(func() {
|
||||
setting.AppUrl = origAppURL
|
||||
setting.PluginsPath = origPluginsPath
|
||||
})
|
||||
setting.AppUrl = "http://localhost:3000/"
|
||||
setting.PluginsPath = "testdata/invalid-v2-signature-2"
|
||||
|
||||
pm := &PluginManager{
|
||||
Cfg: &setting.Cfg{},
|
||||
Cfg: &setting.Cfg{
|
||||
PluginsPath: "testdata/invalid-v2-signature-2",
|
||||
},
|
||||
BackendPluginManager: &fakeBackendPluginManager{},
|
||||
}
|
||||
err := pm.Init()
|
||||
|
@ -112,17 +112,16 @@ func (pm *PluginManager) checkForUpdates() {
|
||||
}
|
||||
|
||||
if strings.Contains(setting.BuildVersion, "-") {
|
||||
GrafanaLatestVersion = githubLatest.Testing
|
||||
GrafanaHasUpdate = !strings.HasPrefix(setting.BuildVersion, githubLatest.Testing)
|
||||
pm.GrafanaLatestVersion = githubLatest.Testing
|
||||
pm.GrafanaHasUpdate = !strings.HasPrefix(setting.BuildVersion, githubLatest.Testing)
|
||||
} else {
|
||||
GrafanaLatestVersion = githubLatest.Stable
|
||||
GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion
|
||||
pm.GrafanaLatestVersion = githubLatest.Stable
|
||||
pm.GrafanaHasUpdate = githubLatest.Stable != setting.BuildVersion
|
||||
}
|
||||
|
||||
currVersion, err1 := version.NewVersion(setting.BuildVersion)
|
||||
latestVersion, err2 := version.NewVersion(GrafanaLatestVersion)
|
||||
|
||||
latestVersion, err2 := version.NewVersion(pm.GrafanaLatestVersion)
|
||||
if err1 == nil && err2 == nil {
|
||||
GrafanaHasUpdate = currVersion.LessThan(latestVersion)
|
||||
pm.GrafanaHasUpdate = currVersion.LessThan(latestVersion)
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,6 @@ var (
|
||||
|
||||
// Paths
|
||||
HomePath string
|
||||
PluginsPath string
|
||||
CustomInitPath = "conf/custom.ini"
|
||||
|
||||
// HTTP server options
|
||||
@ -215,6 +214,7 @@ type Cfg struct {
|
||||
ProvisioningPath string
|
||||
DataPath string
|
||||
LogsPath string
|
||||
PluginsPath string
|
||||
BundledPluginsPath string
|
||||
|
||||
// SMTP email settings
|
||||
@ -765,7 +765,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
|
||||
cfg.Env = Env
|
||||
InstanceName = valueAsString(iniFile.Section(""), "instance_name", "unknown_instance_name")
|
||||
plugins := valueAsString(iniFile.Section("paths"), "plugins", "")
|
||||
PluginsPath = makeAbsolute(plugins, HomePath)
|
||||
cfg.PluginsPath = makeAbsolute(plugins, HomePath)
|
||||
cfg.BundledPluginsPath = makeAbsolute("plugins-bundled", HomePath)
|
||||
provisioning := valueAsString(iniFile.Section("paths"), "provisioning", "")
|
||||
cfg.ProvisioningPath = makeAbsolute(provisioning, HomePath)
|
||||
@ -969,7 +969,7 @@ func (cfg *Cfg) LogConfigSources() {
|
||||
cfg.Logger.Info("Path Home", "path", HomePath)
|
||||
cfg.Logger.Info("Path Data", "path", cfg.DataPath)
|
||||
cfg.Logger.Info("Path Logs", "path", cfg.LogsPath)
|
||||
cfg.Logger.Info("Path Plugins", "path", PluginsPath)
|
||||
cfg.Logger.Info("Path Plugins", "path", cfg.PluginsPath)
|
||||
cfg.Logger.Info("Path Provisioning", "path", cfg.ProvisioningPath)
|
||||
cfg.Logger.Info("App mode " + cfg.Env)
|
||||
}
|
||||
|
Reference in New Issue
Block a user