[WIP] Plugins: Introduce Plugins specific config (#54854)

This commit is contained in:
Will Browne 2022-09-14 14:35:35 +01:00 committed by GitHub
parent 8b38f9408d
commit 0571d98bba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 243 additions and 173 deletions

View File

@ -33,6 +33,7 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
pluginsCfg "github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/manager" "github.com/grafana/grafana/pkg/plugins/manager"
"github.com/grafana/grafana/pkg/plugins/manager/client" "github.com/grafana/grafana/pkg/plugins/manager/client"
pluginDashboards "github.com/grafana/grafana/pkg/plugins/manager/dashboards" pluginDashboards "github.com/grafana/grafana/pkg/plugins/manager/dashboards"
@ -175,6 +176,7 @@ var wireSet = wire.NewSet(
updatechecker.ProvideGrafanaService, updatechecker.ProvideGrafanaService,
updatechecker.ProvidePluginsService, updatechecker.ProvidePluginsService,
uss.ProvideService, uss.ProvideService,
pluginsCfg.ProvideConfig,
registry.ProvideService, registry.ProvideService,
wire.Bind(new(registry.Service), new(*registry.InMemory)), wire.Bind(new(registry.Service), new(*registry.InMemory)),
repo.ProvideService, repo.ProvideService,

View File

@ -1,53 +0,0 @@
package plugins
import (
"github.com/grafana/grafana-azure-sdk-go/azsettings"
"github.com/grafana/grafana/pkg/setting"
)
type Cfg struct {
DevMode bool
PluginsPath string
PluginSettings setting.PluginSettings
PluginsAllowUnsigned []string
EnterpriseLicensePath string
// AWS Plugin Auth
AWSAllowedAuthProviders []string
AWSAssumeRoleEnabled bool
// Azure Cloud settings
Azure *azsettings.AzureSettings
BuildVersion string // TODO Remove
}
func NewCfg() *Cfg {
return &Cfg{}
}
func FromGrafanaCfg(grafanaCfg *setting.Cfg) *Cfg {
cfg := &Cfg{}
cfg.DevMode = grafanaCfg.Env == setting.Dev
cfg.PluginsPath = grafanaCfg.PluginsPath
cfg.PluginSettings = grafanaCfg.PluginSettings
cfg.PluginsAllowUnsigned = grafanaCfg.PluginsAllowUnsigned
cfg.EnterpriseLicensePath = grafanaCfg.EnterpriseLicensePath
// AWS
cfg.AWSAllowedAuthProviders = grafanaCfg.AWSAllowedAuthProviders
cfg.AWSAssumeRoleEnabled = grafanaCfg.AWSAssumeRoleEnabled
// Azure
cfg.Azure = grafanaCfg.Azure
cfg.BuildVersion = grafanaCfg.BuildVersion
return cfg
}

View File

@ -0,0 +1,86 @@
package config
import (
"strings"
"github.com/grafana/grafana-azure-sdk-go/azsettings"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
)
type Cfg struct {
log log.Logger
DevMode bool
PluginSettings setting.PluginSettings
PluginsAllowUnsigned []string
EnterpriseLicensePath string
// AWS Plugin Auth
AWSAllowedAuthProviders []string
AWSAssumeRoleEnabled bool
// Azure Cloud settings
Azure *azsettings.AzureSettings
BuildVersion string // TODO Remove
}
func ProvideConfig(settingProvider setting.Provider, grafanaCfg *setting.Cfg) *Cfg {
return NewCfg(settingProvider, grafanaCfg)
}
func NewCfg(settingProvider setting.Provider, grafanaCfg *setting.Cfg) *Cfg {
logger := log.New("plugin.cfg")
azure := settingProvider.Section("azure")
aws := settingProvider.Section("aws")
plugins := settingProvider.Section("plugins")
allowedUnsigned := grafanaCfg.PluginsAllowUnsigned
if len(plugins.KeyValue("allow_loading_unsigned_plugins").Value()) > 0 {
allowedUnsigned = strings.Split(plugins.KeyValue("allow_loading_unsigned_plugins").Value(), ",")
}
allowedAuth := grafanaCfg.AWSAllowedAuthProviders
if len(aws.KeyValue("allowed_auth_providers").Value()) > 0 {
allowedUnsigned = strings.Split(settingProvider.KeyValue("plugins", "allow_loading_unsigned_plugins").Value(), ",")
}
return &Cfg{
log: logger,
BuildVersion: grafanaCfg.BuildVersion,
DevMode: settingProvider.KeyValue("", "app_mode").MustBool(grafanaCfg.Env == setting.Dev),
EnterpriseLicensePath: settingProvider.KeyValue("enterprise", "license_path").MustString(grafanaCfg.EnterpriseLicensePath),
PluginSettings: extractPluginSettings(settingProvider),
PluginsAllowUnsigned: allowedUnsigned,
AWSAllowedAuthProviders: allowedAuth,
AWSAssumeRoleEnabled: aws.KeyValue("assume_role_enabled").MustBool(grafanaCfg.AWSAssumeRoleEnabled),
Azure: &azsettings.AzureSettings{
Cloud: azure.KeyValue("cloud").MustString(grafanaCfg.Azure.Cloud),
ManagedIdentityEnabled: azure.KeyValue("managed_identity_enabled").MustBool(grafanaCfg.Azure.ManagedIdentityEnabled),
ManagedIdentityClientId: azure.KeyValue("managed_identity_client_id").MustString(grafanaCfg.Azure.ManagedIdentityClientId),
},
}
}
func extractPluginSettings(settingProvider setting.Provider) setting.PluginSettings {
ps := setting.PluginSettings{}
for sectionName, sectionCopy := range settingProvider.Current() {
if !strings.HasPrefix(sectionName, "plugin.") {
continue
}
// Calling Current() returns a redacted version of section. We need to replace the map values with the unredacted values.
section := settingProvider.Section(sectionName)
for k := range sectionCopy {
sectionCopy[k] = section.KeyValue(k).MustString("")
}
pluginID := strings.Replace(sectionName, "plugin.", "", 1)
ps[pluginID] = sectionCopy
}
return ps
}

View File

@ -0,0 +1,38 @@
package config
import (
"testing"
"gopkg.in/ini.v1"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
)
func TestPluginSettings(t *testing.T) {
raw, err := ini.Load([]byte(`
[plugins]
test_key = 123
[plugin.test-datasource]
foo = 5m
bar = something
[plugin.secret-plugin]
secret_key = secret
normal_key = not a secret`))
require.NoError(t, err)
cfg := &setting.Cfg{Raw: raw}
settings := &setting.OSSImpl{Cfg: cfg}
ps := extractPluginSettings(settings)
require.Len(t, ps, 2)
require.Len(t, ps["test-datasource"], 2)
require.Equal(t, ps["test-datasource"]["foo"], "5m")
require.Equal(t, ps["test-datasource"]["bar"], "something")
require.Len(t, ps["secret-plugin"], 2)
require.Equal(t, ps["secret-plugin"]["secret_key"], "secret")
require.Equal(t, ps["secret-plugin"]["normal_key"], "not a secret")
}

View File

@ -12,16 +12,17 @@ import (
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/config"
) )
type Initializer struct { type Initializer struct {
cfg *plugins.Cfg cfg *config.Cfg
license models.Licensing license models.Licensing
backendProvider plugins.BackendFactoryProvider backendProvider plugins.BackendFactoryProvider
log log.Logger log log.Logger
} }
func New(cfg *plugins.Cfg, backendProvider plugins.BackendFactoryProvider, license models.Licensing) Initializer { func New(cfg *config.Cfg, backendProvider plugins.BackendFactoryProvider, license models.Licensing) Initializer {
return Initializer{ return Initializer{
cfg: cfg, cfg: cfg,
license: license, license: license,
@ -101,7 +102,7 @@ func (ps pluginSettings) asEnvVar(prefix string, hostEnv []string) []string {
return env return env
} }
func getPluginSettings(pluginID string, cfg *plugins.Cfg) pluginSettings { func getPluginSettings(pluginID string, cfg *config.Cfg) pluginSettings {
ps := pluginSettings{} ps := pluginSettings{}
for k, v := range cfg.PluginSettings[pluginID] { for k, v := range cfg.PluginSettings[pluginID] {
if k == "path" || strings.ToLower(k) == "id" { if k == "path" || strings.ToLower(k) == "id" {

View File

@ -10,6 +10,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/config"
) )
func TestInitializer_Initialize(t *testing.T) { func TestInitializer_Initialize(t *testing.T) {
@ -34,7 +35,7 @@ func TestInitializer_Initialize(t *testing.T) {
} }
i := &Initializer{ i := &Initializer{
cfg: plugins.NewCfg(), cfg: &config.Cfg{},
log: log.NewNopLogger(), log: log.NewNopLogger(),
backendProvider: &fakeBackendProvider{ backendProvider: &fakeBackendProvider{
plugin: p, plugin: p,
@ -64,7 +65,7 @@ func TestInitializer_Initialize(t *testing.T) {
} }
i := &Initializer{ i := &Initializer{
cfg: plugins.NewCfg(), cfg: &config.Cfg{},
log: log.NewNopLogger(), log: log.NewNopLogger(),
backendProvider: &fakeBackendProvider{ backendProvider: &fakeBackendProvider{
plugin: p, plugin: p,
@ -94,7 +95,7 @@ func TestInitializer_Initialize(t *testing.T) {
} }
i := &Initializer{ i := &Initializer{
cfg: plugins.NewCfg(), cfg: &config.Cfg{},
log: log.NewNopLogger(), log: log.NewNopLogger(),
backendProvider: &fakeBackendProvider{ backendProvider: &fakeBackendProvider{
plugin: p, plugin: p,
@ -117,7 +118,7 @@ func TestInitializer_Initialize(t *testing.T) {
} }
i := &Initializer{ i := &Initializer{
cfg: &plugins.Cfg{}, cfg: &config.Cfg{},
log: log.NewNopLogger(), log: log.NewNopLogger(),
backendProvider: &fakeBackendProvider{ backendProvider: &fakeBackendProvider{
plugin: p, plugin: p,
@ -147,7 +148,7 @@ func TestInitializer_envVars(t *testing.T) {
} }
i := &Initializer{ i := &Initializer{
cfg: &plugins.Cfg{ cfg: &config.Cfg{
EnterpriseLicensePath: "/path/to/ent/license", EnterpriseLicensePath: "/path/to/ent/license",
PluginSettings: map[string]map[string]string{ PluginSettings: map[string]map[string]string{
"test": { "test": {

View File

@ -19,11 +19,11 @@ import (
"github.com/grafana/grafana/pkg/infra/metrics" "github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/manager/loader/finder" "github.com/grafana/grafana/pkg/plugins/manager/loader/finder"
"github.com/grafana/grafana/pkg/plugins/manager/loader/initializer" "github.com/grafana/grafana/pkg/plugins/manager/loader/initializer"
"github.com/grafana/grafana/pkg/plugins/manager/signature" "github.com/grafana/grafana/pkg/plugins/manager/signature"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
@ -35,7 +35,6 @@ var (
var _ plugins.ErrorResolver = (*Loader)(nil) var _ plugins.ErrorResolver = (*Loader)(nil)
type Loader struct { type Loader struct {
cfg *plugins.Cfg
pluginFinder finder.Finder pluginFinder finder.Finder
pluginInitializer initializer.Initializer pluginInitializer initializer.Initializer
signatureValidator signature.Validator signatureValidator signature.Validator
@ -44,15 +43,14 @@ type Loader struct {
errs map[string]*plugins.SignatureError errs map[string]*plugins.SignatureError
} }
func ProvideService(cfg *setting.Cfg, license models.Licensing, authorizer plugins.PluginLoaderAuthorizer, func ProvideService(cfg *config.Cfg, license models.Licensing, authorizer plugins.PluginLoaderAuthorizer,
backendProvider plugins.BackendFactoryProvider) (*Loader, error) { backendProvider plugins.BackendFactoryProvider) (*Loader, error) {
return New(plugins.FromGrafanaCfg(cfg), license, authorizer, backendProvider), nil return New(cfg, license, authorizer, backendProvider), nil
} }
func New(cfg *plugins.Cfg, license models.Licensing, authorizer plugins.PluginLoaderAuthorizer, func New(cfg *config.Cfg, license models.Licensing, authorizer plugins.PluginLoaderAuthorizer,
backendProvider plugins.BackendFactoryProvider) *Loader { backendProvider plugins.BackendFactoryProvider) *Loader {
return &Loader{ return &Loader{
cfg: cfg,
pluginFinder: finder.New(), pluginFinder: finder.New(),
pluginInitializer: initializer.New(cfg, backendProvider, license), pluginInitializer: initializer.New(cfg, backendProvider, license),
signatureValidator: signature.NewValidator(authorizer), signatureValidator: signature.NewValidator(authorizer),

View File

@ -7,21 +7,21 @@ import (
"sort" "sort"
"testing" "testing"
"github.com/grafana/grafana/pkg/infra/log/logtest"
"github.com/grafana/grafana/pkg/services/org"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts" "github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/infra/log/logtest"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider" "github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/manager/loader/finder" "github.com/grafana/grafana/pkg/plugins/manager/loader/finder"
"github.com/grafana/grafana/pkg/plugins/manager/loader/initializer" "github.com/grafana/grafana/pkg/plugins/manager/loader/initializer"
"github.com/grafana/grafana/pkg/plugins/manager/signature" "github.com/grafana/grafana/pkg/plugins/manager/signature"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
@ -41,18 +41,16 @@ func TestLoader_Load(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
class plugins.Class class plugins.Class
cfg *plugins.Cfg cfg *config.Cfg
pluginPaths []string pluginPaths []string
existingPlugins map[string]struct{} existingPlugins map[string]struct{}
want []*plugins.Plugin want []*plugins.Plugin
pluginErrors map[string]*plugins.Error pluginErrors map[string]*plugins.Error
}{ }{
{ {
name: "Load a Core plugin", name: "Load a Core plugin",
class: plugins.Core, class: plugins.Core,
cfg: &plugins.Cfg{ cfg: &config.Cfg{},
PluginsPath: corePluginDir,
},
pluginPaths: []string{filepath.Join(corePluginDir, "app/plugins/datasource/cloudwatch")}, pluginPaths: []string{filepath.Join(corePluginDir, "app/plugins/datasource/cloudwatch")},
want: []*plugins.Plugin{ want: []*plugins.Plugin{
{ {
@ -99,11 +97,9 @@ func TestLoader_Load(t *testing.T) {
}, },
}, },
{ {
name: "Load a Bundled plugin", name: "Load a Bundled plugin",
class: plugins.Bundled, class: plugins.Bundled,
cfg: &plugins.Cfg{ cfg: &config.Cfg{},
PluginsPath: filepath.Join(parentDir, "testdata"),
},
pluginPaths: []string{"../testdata/valid-v2-signature"}, pluginPaths: []string{"../testdata/valid-v2-signature"},
want: []*plugins.Plugin{ want: []*plugins.Plugin{
{ {
@ -141,11 +137,9 @@ func TestLoader_Load(t *testing.T) {
}, },
}, },
}, { }, {
name: "Load plugin with symbolic links", name: "Load plugin with symbolic links",
class: plugins.External, class: plugins.External,
cfg: &plugins.Cfg{ cfg: &config.Cfg{},
PluginsPath: filepath.Join(parentDir),
},
pluginPaths: []string{"../testdata/symbolic-plugin-dirs"}, pluginPaths: []string{"../testdata/symbolic-plugin-dirs"},
want: []*plugins.Plugin{ want: []*plugins.Plugin{
{ {
@ -221,9 +215,8 @@ func TestLoader_Load(t *testing.T) {
}, { }, {
name: "Load an unsigned plugin (development)", name: "Load an unsigned plugin (development)",
class: plugins.External, class: plugins.External,
cfg: &plugins.Cfg{ cfg: &config.Cfg{
DevMode: true, DevMode: true,
PluginsPath: filepath.Join(parentDir),
}, },
pluginPaths: []string{"../testdata/unsigned-datasource"}, pluginPaths: []string{"../testdata/unsigned-datasource"},
want: []*plugins.Plugin{ want: []*plugins.Plugin{
@ -258,11 +251,9 @@ func TestLoader_Load(t *testing.T) {
}, },
}, },
}, { }, {
name: "Load an unsigned plugin (production)", name: "Load an unsigned plugin (production)",
class: plugins.External, class: plugins.External,
cfg: &plugins.Cfg{ cfg: &config.Cfg{},
PluginsPath: filepath.Join(parentDir),
},
pluginPaths: []string{"../testdata/unsigned-datasource"}, pluginPaths: []string{"../testdata/unsigned-datasource"},
want: []*plugins.Plugin{}, want: []*plugins.Plugin{},
pluginErrors: map[string]*plugins.Error{ pluginErrors: map[string]*plugins.Error{
@ -275,8 +266,7 @@ func TestLoader_Load(t *testing.T) {
{ {
name: "Load an unsigned plugin using PluginsAllowUnsigned config (production)", name: "Load an unsigned plugin using PluginsAllowUnsigned config (production)",
class: plugins.External, class: plugins.External,
cfg: &plugins.Cfg{ cfg: &config.Cfg{
PluginsPath: filepath.Join(parentDir),
PluginsAllowUnsigned: []string{"test-datasource"}, PluginsAllowUnsigned: []string{"test-datasource"},
}, },
pluginPaths: []string{"../testdata/unsigned-datasource"}, pluginPaths: []string{"../testdata/unsigned-datasource"},
@ -313,11 +303,9 @@ func TestLoader_Load(t *testing.T) {
}, },
}, },
{ {
name: "Load an unsigned plugin with modified signature (production)", name: "Load an unsigned plugin with modified signature (production)",
class: plugins.External, class: plugins.External,
cfg: &plugins.Cfg{ cfg: &config.Cfg{},
PluginsPath: filepath.Join(parentDir),
},
pluginPaths: []string{"../testdata/lacking-files"}, pluginPaths: []string{"../testdata/lacking-files"},
want: []*plugins.Plugin{}, want: []*plugins.Plugin{},
pluginErrors: map[string]*plugins.Error{ pluginErrors: map[string]*plugins.Error{
@ -330,8 +318,7 @@ func TestLoader_Load(t *testing.T) {
{ {
name: "Load an unsigned plugin with modified signature using PluginsAllowUnsigned config (production) still includes a signing error", name: "Load an unsigned plugin with modified signature using PluginsAllowUnsigned config (production) still includes a signing error",
class: plugins.External, class: plugins.External,
cfg: &plugins.Cfg{ cfg: &config.Cfg{
PluginsPath: filepath.Join(parentDir),
PluginsAllowUnsigned: []string{"test-datasource"}, PluginsAllowUnsigned: []string{"test-datasource"},
}, },
pluginPaths: []string{"../testdata/lacking-files"}, pluginPaths: []string{"../testdata/lacking-files"},
@ -346,8 +333,7 @@ func TestLoader_Load(t *testing.T) {
{ {
name: "Load a plugin with manifest which has a file not found in plugin folder", name: "Load a plugin with manifest which has a file not found in plugin folder",
class: plugins.External, class: plugins.External,
cfg: &plugins.Cfg{ cfg: &config.Cfg{
PluginsPath: filepath.Join(parentDir),
PluginsAllowUnsigned: []string{"test-datasource"}, PluginsAllowUnsigned: []string{"test-datasource"},
}, },
pluginPaths: []string{"../testdata/invalid-v2-missing-file"}, pluginPaths: []string{"../testdata/invalid-v2-missing-file"},
@ -362,8 +348,7 @@ func TestLoader_Load(t *testing.T) {
{ {
name: "Load a plugin with file which is missing from the manifest", name: "Load a plugin with file which is missing from the manifest",
class: plugins.External, class: plugins.External,
cfg: &plugins.Cfg{ cfg: &config.Cfg{
PluginsPath: filepath.Join(parentDir),
PluginsAllowUnsigned: []string{"test-datasource"}, PluginsAllowUnsigned: []string{"test-datasource"},
}, },
pluginPaths: []string{"../testdata/invalid-v2-extra-file"}, pluginPaths: []string{"../testdata/invalid-v2-extra-file"},
@ -378,8 +363,7 @@ func TestLoader_Load(t *testing.T) {
{ {
name: "Load an app with includes", name: "Load an app with includes",
class: plugins.External, class: plugins.External,
cfg: &plugins.Cfg{ cfg: &config.Cfg{
PluginsPath: filepath.Join(parentDir),
PluginsAllowUnsigned: []string{"test-app"}, PluginsAllowUnsigned: []string{"test-app"},
}, },
pluginPaths: []string{"../testdata/test-app-with-includes"}, pluginPaths: []string{"../testdata/test-app-with-includes"},
@ -509,7 +493,7 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
t.Run("Load multiple", func(t *testing.T) { t.Run("Load multiple", func(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
cfg *plugins.Cfg cfg *config.Cfg
pluginPaths []string pluginPaths []string
appURL string appURL string
existingPlugins map[string]struct{} existingPlugins map[string]struct{}
@ -517,10 +501,8 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
pluginErrors map[string]*plugins.Error pluginErrors map[string]*plugins.Error
}{ }{
{ {
name: "Load multiple plugins (broken, valid, unsigned)", name: "Load multiple plugins (broken, valid, unsigned)",
cfg: &plugins.Cfg{ cfg: &config.Cfg{},
PluginsPath: filepath.Join(parentDir),
},
appURL: "http://localhost:3000", appURL: "http://localhost:3000",
pluginPaths: []string{ pluginPaths: []string{
"../testdata/invalid-plugin-json", // test-app "../testdata/invalid-plugin-json", // test-app
@ -648,7 +630,7 @@ func TestLoader_Signature_RootURL(t *testing.T) {
}, },
} }
l := newLoader(&plugins.Cfg{PluginsPath: filepath.Join(parentDir)}) l := newLoader(&config.Cfg{})
got, err := l.Load(context.Background(), plugins.External, paths, map[string]struct{}{}) got, err := l.Load(context.Background(), plugins.External, paths, map[string]struct{}{})
assert.NoError(t, err) assert.NoError(t, err)
@ -717,9 +699,7 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) {
}, },
} }
l := newLoader(&plugins.Cfg{ l := newLoader(&config.Cfg{})
PluginsPath: filepath.Dir(pluginDir),
})
got, err := l.Load(context.Background(), plugins.External, []string{pluginDir, pluginDir}, map[string]struct{}{}) got, err := l.Load(context.Background(), plugins.External, []string{pluginDir, pluginDir}, map[string]struct{}{})
assert.NoError(t, err) assert.NoError(t, err)
@ -806,9 +786,7 @@ func TestLoader_loadNestedPlugins(t *testing.T) {
t.Run("Load nested External plugins", func(t *testing.T) { t.Run("Load nested External plugins", func(t *testing.T) {
expected := []*plugins.Plugin{parent, child} expected := []*plugins.Plugin{parent, child}
l := newLoader(&plugins.Cfg{ l := newLoader(&config.Cfg{})
PluginsPath: rootDir,
})
got, err := l.Load(context.Background(), plugins.External, []string{"../testdata/nested-plugins"}, map[string]struct{}{}) got, err := l.Load(context.Background(), plugins.External, []string{"../testdata/nested-plugins"}, map[string]struct{}{})
assert.NoError(t, err) assert.NoError(t, err)
@ -828,9 +806,7 @@ func TestLoader_loadNestedPlugins(t *testing.T) {
parent.Children = nil parent.Children = nil
expected := []*plugins.Plugin{parent} expected := []*plugins.Plugin{parent}
l := newLoader(&plugins.Cfg{ l := newLoader(&config.Cfg{})
PluginsPath: rootDir,
})
got, err := l.Load(context.Background(), plugins.External, []string{"../testdata/nested-plugins"}, map[string]struct{}{ got, err := l.Load(context.Background(), plugins.External, []string{"../testdata/nested-plugins"}, map[string]struct{}{
"test-panel": {}, "test-panel": {},
@ -970,9 +946,7 @@ func TestLoader_loadNestedPlugins(t *testing.T) {
child.Parent = parent child.Parent = parent
expected := []*plugins.Plugin{parent, child} expected := []*plugins.Plugin{parent, child}
l := newLoader(&plugins.Cfg{ l := newLoader(&config.Cfg{})
PluginsPath: rootDir,
})
got, err := l.Load(context.Background(), plugins.External, []string{"../testdata/app-with-child"}, map[string]struct{}{}) got, err := l.Load(context.Background(), plugins.External, []string{"../testdata/app-with-child"}, map[string]struct{}{})
assert.NoError(t, err) assert.NoError(t, err)
@ -1169,9 +1143,8 @@ func Test_setPathsBasedOnApp(t *testing.T) {
}) })
} }
func newLoader(cfg *plugins.Cfg) *Loader { func newLoader(cfg *config.Cfg) *Loader {
return &Loader{ return &Loader{
cfg: cfg,
pluginFinder: finder.New(), pluginFinder: finder.New(),
pluginInitializer: initializer.New(cfg, provider.ProvideService(coreplugin.NewRegistry(make(map[string]backendplugin.PluginFactoryFunc))), &fakeLicensingService{}), pluginInitializer: initializer.New(cfg, provider.ProvideService(coreplugin.NewRegistry(make(map[string]backendplugin.PluginFactoryFunc))), &fakeLicensingService{}),
signatureValidator: signature.NewValidator(signature.NewUnsignedAuthorizer(cfg)), signatureValidator: signature.NewValidator(signature.NewUnsignedAuthorizer(cfg)),

View File

@ -7,6 +7,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/logger" "github.com/grafana/grafana/pkg/plugins/logger"
"github.com/grafana/grafana/pkg/plugins/manager/loader" "github.com/grafana/grafana/pkg/plugins/manager/loader"
"github.com/grafana/grafana/pkg/plugins/manager/process" "github.com/grafana/grafana/pkg/plugins/manager/process"
@ -21,7 +22,7 @@ var _ plugins.RendererManager = (*PluginManager)(nil)
var _ plugins.SecretsPluginManager = (*PluginManager)(nil) var _ plugins.SecretsPluginManager = (*PluginManager)(nil)
type PluginManager struct { type PluginManager struct {
cfg *plugins.Cfg cfg *config.Cfg
pluginSources []plugins.PluginSource pluginSources []plugins.PluginSource
pluginRepo repo.Service pluginRepo repo.Service
pluginStorage storage.Manager pluginStorage storage.Manager
@ -31,10 +32,15 @@ type PluginManager struct {
log log.Logger log log.Logger
} }
func ProvideService(grafanaCfg *setting.Cfg, pluginRegistry registry.Service, pluginLoader loader.Service, func ProvideService(cfg *config.Cfg, grafCfg *setting.Cfg, pluginRegistry registry.Service, pluginLoader loader.Service,
pluginRepo repo.Service) (*PluginManager, error) { pluginRepo repo.Service) (*PluginManager, error) {
pm := New(plugins.FromGrafanaCfg(grafanaCfg), pluginRegistry, pluginSources(grafanaCfg), pluginLoader, pm := New(cfg, pluginRegistry,
pluginRepo, storage.FileSystem(logger.NewLogger("plugin.fs"), grafanaCfg.PluginsPath), pluginSources(pathData{
pluginsPath: grafCfg.PluginsPath,
bundledPluginsPath: grafCfg.BundledPluginsPath,
staticRootPath: grafCfg.StaticRootPath,
}, cfg.PluginSettings),
pluginLoader, pluginRepo, storage.FileSystem(logger.NewLogger("plugin.fs"), grafCfg.PluginsPath),
process.NewManager(pluginRegistry), process.NewManager(pluginRegistry),
) )
if err := pm.Init(context.Background()); err != nil { if err := pm.Init(context.Background()); err != nil {
@ -43,7 +49,7 @@ func ProvideService(grafanaCfg *setting.Cfg, pluginRegistry registry.Service, pl
return pm, nil return pm, nil
} }
func New(cfg *plugins.Cfg, pluginRegistry registry.Service, pluginSources []plugins.PluginSource, func New(cfg *config.Cfg, pluginRegistry registry.Service, pluginSources []plugins.PluginSource,
pluginLoader loader.Service, pluginRepo repo.Service, pluginStorage storage.Manager, pluginLoader loader.Service, pluginRepo repo.Service, pluginStorage storage.Manager,
processManager process.Service) *PluginManager { processManager process.Service) *PluginManager {
return &PluginManager{ return &PluginManager{
@ -253,26 +259,30 @@ func (m *PluginManager) unregisterAndStop(ctx context.Context, p *plugins.Plugin
return nil return nil
} }
func pluginSources(cfg *setting.Cfg) []plugins.PluginSource { type pathData struct {
pluginsPath, bundledPluginsPath, staticRootPath string
}
func pluginSources(p pathData, ps map[string]map[string]string) []plugins.PluginSource {
return []plugins.PluginSource{ return []plugins.PluginSource{
{Class: plugins.Core, Paths: corePluginPaths(cfg)}, {Class: plugins.Core, Paths: corePluginPaths(p.staticRootPath)},
{Class: plugins.Bundled, Paths: []string{cfg.BundledPluginsPath}}, {Class: plugins.Bundled, Paths: []string{p.bundledPluginsPath}},
{Class: plugins.External, Paths: append([]string{cfg.PluginsPath}, pluginSettingPaths(cfg)...)}, {Class: plugins.External, Paths: append([]string{p.pluginsPath}, pluginSettingPaths(ps)...)},
} }
} }
// corePluginPaths provides a list of the Core plugin paths which need to be scanned on init() // corePluginPaths provides a list of the Core plugin paths which need to be scanned on init()
func corePluginPaths(cfg *setting.Cfg) []string { func corePluginPaths(staticRootPath string) []string {
datasourcePaths := filepath.Join(cfg.StaticRootPath, "app/plugins/datasource") datasourcePaths := filepath.Join(staticRootPath, "app/plugins/datasource")
panelsPath := filepath.Join(cfg.StaticRootPath, "app/plugins/panel") panelsPath := filepath.Join(staticRootPath, "app/plugins/panel")
return []string{datasourcePaths, panelsPath} return []string{datasourcePaths, panelsPath}
} }
// pluginSettingPaths provides a plugin paths defined in cfg.PluginSettings which need to be scanned on init() // pluginSettingPaths provides a plugin paths defined in cfg.PluginSettings which need to be scanned on init()
func pluginSettingPaths(cfg *setting.Cfg) []string { func pluginSettingPaths(ps map[string]map[string]string) []string {
var pluginSettingDirs []string var pluginSettingDirs []string
for _, settings := range cfg.PluginSettings { for _, s := range ps {
path, exists := settings["path"] path, exists := s["path"]
if !exists || path == "" { if !exists || path == "" {
continue continue
} }

View File

@ -9,17 +9,19 @@ import (
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/grafana/grafana-azure-sdk-go/azsettings"
"github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"
"github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider" "github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/manager/client" "github.com/grafana/grafana/pkg/plugins/manager/client"
"github.com/grafana/grafana/pkg/plugins/manager/loader" "github.com/grafana/grafana/pkg/plugins/manager/loader"
"github.com/grafana/grafana/pkg/plugins/manager/registry" "github.com/grafana/grafana/pkg/plugins/manager/registry"
@ -57,20 +59,27 @@ func TestIntegrationPluginManager_Run(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
features := featuremgmt.WithFeatures() features := featuremgmt.WithFeatures()
// We use the raw config here as it forms the basis for the setting.Provider implementation
// The plugin manager also relies directly on the setting.Cfg struct to provide Grafana specific
// properties such as the loading paths
raw, err := ini.Load([]byte(`
app_mode = production
[plugin.test-app]
path=testdata/test-app
[plugin.test-panel]
not=included
`),
)
require.NoError(t, err)
cfg := &setting.Cfg{ cfg := &setting.Cfg{
Raw: ini.Empty(), Raw: raw,
Env: setting.Prod, StaticRootPath: staticRootPath,
StaticRootPath: staticRootPath, BundledPluginsPath: bundledPluginsPath,
BundledPluginsPath: bundledPluginsPath, Azure: &azsettings.AzureSettings{},
IsFeatureToggleEnabled: features.IsEnabled,
PluginSettings: map[string]map[string]string{
"plugin.test-app": {
"path": "testdata/test-app",
},
"plugin.test-panel": {
"not": "included",
},
},
} }
tracer := &fakeTracer{} tracer := &fakeTracer{}
@ -99,9 +108,9 @@ func TestIntegrationPluginManager_Run(t *testing.T) {
coreRegistry := coreplugin.ProvideCoreRegistry(am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf) coreRegistry := coreplugin.ProvideCoreRegistry(am, cw, cm, es, grap, idb, lk, otsdb, pr, tmpo, td, pg, my, ms, graf)
pmCfg := plugins.FromGrafanaCfg(cfg) pCfg := config.ProvideConfig(setting.ProvideProvider(cfg), cfg)
reg := registry.ProvideService() reg := registry.ProvideService()
pm, err := ProvideService(cfg, reg, loader.New(pmCfg, license, signature.NewUnsignedAuthorizer(pmCfg), pm, err := ProvideService(pCfg, cfg, reg, loader.New(pCfg, license, signature.NewUnsignedAuthorizer(pCfg),
provider.ProvideService(coreRegistry)), nil) provider.ProvideService(coreRegistry)), nil)
require.NoError(t, err) require.NoError(t, err)
ps := store.ProvideService(reg) ps := store.ProvideService(reg)

View File

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/manager/fakes" "github.com/grafana/grafana/pkg/plugins/manager/fakes"
"github.com/grafana/grafana/pkg/plugins/repo" "github.com/grafana/grafana/pkg/plugins/repo"
"github.com/grafana/grafana/pkg/plugins/storage" "github.com/grafana/grafana/pkg/plugins/storage"
@ -61,7 +62,7 @@ func TestPluginManager_Add_Remove(t *testing.T) {
} }
proc := fakes.NewFakeProcessManager() proc := fakes.NewFakeProcessManager()
pm := New(&plugins.Cfg{}, fakes.NewFakePluginRegistry(), []plugins.PluginSource{}, loader, pluginRepo, fs, proc) pm := New(&config.Cfg{}, fakes.NewFakePluginRegistry(), []plugins.PluginSource{}, loader, pluginRepo, fs, proc)
err := pm.Add(context.Background(), pluginID, v1, plugins.CompatOpts{}) err := pm.Add(context.Background(), pluginID, v1, plugins.CompatOpts{})
require.NoError(t, err) require.NoError(t, err)
@ -175,7 +176,7 @@ func TestPluginManager_Add_Remove(t *testing.T) {
} }
proc := fakes.NewFakeProcessManager() proc := fakes.NewFakeProcessManager()
pm := New(&plugins.Cfg{}, reg, []plugins.PluginSource{}, &fakes.FakeLoader{}, &fakes.FakePluginRepo{}, &fakes.FakePluginStorage{}, proc) pm := New(&config.Cfg{}, reg, []plugins.PluginSource{}, &fakes.FakeLoader{}, &fakes.FakePluginRepo{}, &fakes.FakePluginStorage{}, proc)
err := pm.Add(context.Background(), p.ID, "3.2.0", plugins.CompatOpts{}) err := pm.Add(context.Background(), p.ID, "3.2.0", plugins.CompatOpts{})
require.ErrorIs(t, err, plugins.ErrInstallCorePlugin) require.ErrorIs(t, err, plugins.ErrInstallCorePlugin)
@ -201,7 +202,7 @@ func TestPluginManager_Add_Remove(t *testing.T) {
func TestPluginManager_Run(t *testing.T) { func TestPluginManager_Run(t *testing.T) {
t.Run("Plugin sources are loaded in order", func(t *testing.T) { t.Run("Plugin sources are loaded in order", func(t *testing.T) {
loader := &fakes.FakeLoader{} loader := &fakes.FakeLoader{}
pm := New(&plugins.Cfg{}, fakes.NewFakePluginRegistry(), []plugins.PluginSource{ pm := New(&config.Cfg{}, fakes.NewFakePluginRegistry(), []plugins.PluginSource{
{Class: plugins.Bundled, Paths: []string{"path1"}}, {Class: plugins.Bundled, Paths: []string{"path1"}},
{Class: plugins.Core, Paths: []string{"path2"}}, {Class: plugins.Core, Paths: []string{"path2"}},
{Class: plugins.External, Paths: []string{"path3"}}, {Class: plugins.External, Paths: []string{"path3"}},
@ -227,7 +228,7 @@ func TestManager_Renderer(t *testing.T) {
}, },
} }
pm := New(&plugins.Cfg{}, reg, []plugins.PluginSource{}, &fakes.FakeLoader{}, &fakes.FakePluginRepo{}, pm := New(&config.Cfg{}, reg, []plugins.PluginSource{}, &fakes.FakeLoader{}, &fakes.FakePluginRepo{},
&fakes.FakePluginStorage{}, &fakes.FakeProcessManager{}) &fakes.FakePluginStorage{}, &fakes.FakeProcessManager{})
r := pm.Renderer(context.Background()) r := pm.Renderer(context.Background())
@ -251,7 +252,7 @@ func TestManager_SecretsManager(t *testing.T) {
}, },
} }
pm := New(&plugins.Cfg{}, reg, []plugins.PluginSource{}, &fakes.FakeLoader{}, &fakes.FakePluginRepo{}, pm := New(&config.Cfg{}, reg, []plugins.PluginSource{}, &fakes.FakeLoader{}, &fakes.FakePluginRepo{},
&fakes.FakePluginStorage{}, &fakes.FakeProcessManager{}) &fakes.FakePluginStorage{}, &fakes.FakeProcessManager{})
r := pm.SecretsManager(context.Background()) r := pm.SecretsManager(context.Background())

View File

@ -2,21 +2,21 @@ package signature
import ( import (
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/plugins/config"
) )
func NewUnsignedAuthorizer(cfg *plugins.Cfg) *UnsignedPluginAuthorizer { func ProvideOSSAuthorizer(cfg *config.Cfg) *UnsignedPluginAuthorizer {
return NewUnsignedAuthorizer(cfg)
}
func NewUnsignedAuthorizer(cfg *config.Cfg) *UnsignedPluginAuthorizer {
return &UnsignedPluginAuthorizer{ return &UnsignedPluginAuthorizer{
cfg: cfg, cfg: cfg,
} }
} }
func ProvideOSSAuthorizer(cfg *setting.Cfg) *UnsignedPluginAuthorizer {
return NewUnsignedAuthorizer(plugins.FromGrafanaCfg(cfg))
}
type UnsignedPluginAuthorizer struct { type UnsignedPluginAuthorizer struct {
cfg *plugins.Cfg cfg *config.Cfg
} }
func (u *UnsignedPluginAuthorizer) CanLoadPlugin(p *plugins.Plugin) bool { func (u *UnsignedPluginAuthorizer) CanLoadPlugin(p *plugins.Plugin) bool {

View File

@ -35,6 +35,7 @@ import (
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin" "github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
pluginsCfg "github.com/grafana/grafana/pkg/plugins/config"
"github.com/grafana/grafana/pkg/plugins/manager" "github.com/grafana/grafana/pkg/plugins/manager"
"github.com/grafana/grafana/pkg/plugins/manager/client" "github.com/grafana/grafana/pkg/plugins/manager/client"
pluginDashboards "github.com/grafana/grafana/pkg/plugins/manager/dashboards" pluginDashboards "github.com/grafana/grafana/pkg/plugins/manager/dashboards"
@ -174,6 +175,7 @@ var wireBasicSet = wire.NewSet(
wire.Bind(new(usagestats.Service), new(*uss.UsageStats)), wire.Bind(new(usagestats.Service), new(*uss.UsageStats)),
registry.ProvideService, registry.ProvideService,
wire.Bind(new(registry.Service), new(*registry.InMemory)), wire.Bind(new(registry.Service), new(*registry.InMemory)),
pluginsCfg.ProvideConfig,
repo.ProvideService, repo.ProvideService,
wire.Bind(new(repo.Service), new(*repo.Manager)), wire.Bind(new(repo.Service), new(*repo.Manager)),
manager.ProvideService, manager.ProvideService,

View File

@ -189,6 +189,8 @@ func CreateGrafDir(t *testing.T, opts ...GrafanaOpts) (string, string) {
require.NoError(t, err) require.NoError(t, err)
_, err = serverSect.NewKey("port", "0") _, err = serverSect.NewKey("port", "0")
require.NoError(t, err) require.NoError(t, err)
_, err = serverSect.NewKey("static_root_path", publicDir)
require.NoError(t, err)
anonSect, err := cfg.NewSection("auth.anonymous") anonSect, err := cfg.NewSection("auth.anonymous")
require.NoError(t, err) require.NoError(t, err)