Plugins: Refactor plugin config into separate env var and request scoped services (#83261)
* seperate services for env + req * merge with main * fix tests * undo changes to golden file * fix linter * remove unused fields * split out new config struct * provide config * undo go mod changes * more renaming * fix tests * undo bra.toml changes * update go.work.sum * undo changes * trigger * apply PR feedback
This commit is contained in:
@@ -47,7 +47,7 @@ type Dynamic struct {
|
||||
mux sync.RWMutex
|
||||
}
|
||||
|
||||
func ProvideDynamic(cfg *config.Cfg, store angularpatternsstore.Service, features featuremgmt.FeatureToggles) (*Dynamic, error) {
|
||||
func ProvideDynamic(cfg *config.PluginManagementCfg, store angularpatternsstore.Service, features featuremgmt.FeatureToggles) (*Dynamic, error) {
|
||||
d := &Dynamic{
|
||||
log: log.New("plugin.angulardetectorsprovider.dynamic"),
|
||||
features: features,
|
||||
|
||||
@@ -574,7 +574,7 @@ func provideDynamic(t *testing.T, gcomURL string, opts ...provideDynamicOpts) *D
|
||||
opt.store = angularpatternsstore.ProvideService(kvstore.NewFakeKVStore())
|
||||
}
|
||||
d, err := ProvideDynamic(
|
||||
&config.Cfg{GrafanaComURL: gcomURL},
|
||||
&config.PluginManagementCfg{GrafanaComURL: gcomURL},
|
||||
opt.store,
|
||||
featuremgmt.WithFeatures(featuremgmt.FlagPluginsDynamicAngularDetectionPatterns),
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ type Service struct {
|
||||
angularinspector.Inspector
|
||||
}
|
||||
|
||||
func ProvideService(cfg *config.Cfg, dynamic *angulardetectorsprovider.Dynamic) (*Service, error) {
|
||||
func ProvideService(cfg *config.PluginManagementCfg, dynamic *angulardetectorsprovider.Dynamic) (*Service, error) {
|
||||
var detectorsProvider angulardetector.DetectorsProvider
|
||||
var err error
|
||||
static := angularinspector.NewDefaultStaticDetectorsProvider()
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
func TestProvideService(t *testing.T) {
|
||||
t.Run("uses hardcoded inspector if feature flag is not present", func(t *testing.T) {
|
||||
pCfg := &config.Cfg{Features: featuremgmt.WithFeatures()}
|
||||
pCfg := &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()}
|
||||
dynamic, err := angulardetectorsprovider.ProvideDynamic(
|
||||
pCfg,
|
||||
angularpatternsstore.ProvideService(kvstore.NewFakeKVStore()),
|
||||
@@ -33,7 +33,7 @@ func TestProvideService(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("uses dynamic inspector with hardcoded fallback if feature flag is present", func(t *testing.T) {
|
||||
pCfg := &config.Cfg{Features: featuremgmt.WithFeatures(
|
||||
pCfg := &config.PluginManagementCfg{Features: featuremgmt.WithFeatures(
|
||||
featuremgmt.FlagPluginsDynamicAngularDetectionPatterns,
|
||||
)}
|
||||
dynamic, err := angulardetectorsprovider.ProvideDynamic(
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
pCfg "github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func ProvideConfig(settingProvider setting.Provider, grafanaCfg *setting.Cfg, features featuremgmt.FeatureToggles) (*pCfg.Cfg, error) {
|
||||
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(), ",")
|
||||
}
|
||||
|
||||
// Get aws settings from settingProvider instead of grafanaCfg
|
||||
aws := settingProvider.Section("aws")
|
||||
allowedAuth := grafanaCfg.AWSAllowedAuthProviders
|
||||
if len(aws.KeyValue("allowed_auth_providers").Value()) > 0 {
|
||||
allowedAuth = util.SplitString(aws.KeyValue("allowed_auth_providers").Value())
|
||||
}
|
||||
if len(allowedAuth) > 0 {
|
||||
allowedUnsigned = strings.Split(settingProvider.KeyValue("plugins", "allow_loading_unsigned_plugins").Value(), ",")
|
||||
}
|
||||
awsForwardSettingsPlugins := grafanaCfg.AWSForwardSettingsPlugins
|
||||
if len(aws.KeyValue("forward_settings_to_plugins").Value()) > 0 {
|
||||
awsForwardSettingsPlugins = util.SplitString(aws.KeyValue("forward_settings_to_plugins").Value())
|
||||
}
|
||||
|
||||
tracingCfg, err := newTracingCfg(grafanaCfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new opentelemetry cfg: %w", err)
|
||||
}
|
||||
|
||||
return pCfg.NewCfg(
|
||||
settingProvider.KeyValue("", "app_mode").MustBool(grafanaCfg.Env == setting.Dev),
|
||||
grafanaCfg.PluginsPath,
|
||||
extractPluginSettings(settingProvider),
|
||||
allowedUnsigned,
|
||||
allowedAuth,
|
||||
aws.KeyValue("assume_role_enabled").MustBool(grafanaCfg.AWSAssumeRoleEnabled),
|
||||
aws.KeyValue("external_id").Value(),
|
||||
aws.KeyValue("session_duration").Value(),
|
||||
aws.KeyValue("list_metrics_page_limit").Value(),
|
||||
awsForwardSettingsPlugins,
|
||||
grafanaCfg.Azure,
|
||||
grafanaCfg.SecureSocksDSProxy,
|
||||
grafanaCfg.BuildVersion,
|
||||
grafanaCfg.PluginLogBackendRequests,
|
||||
grafanaCfg.PluginsCDNURLTemplate,
|
||||
grafanaCfg.AppURL,
|
||||
grafanaCfg.AppSubURL,
|
||||
tracingCfg,
|
||||
features,
|
||||
grafanaCfg.AngularSupportEnabled,
|
||||
grafanaCfg.GrafanaComURL,
|
||||
grafanaCfg.DisablePlugins,
|
||||
grafanaCfg.HideAngularDeprecation,
|
||||
grafanaCfg.ForwardHostEnvVars,
|
||||
grafanaCfg.ConcurrentQueryCount,
|
||||
grafanaCfg.AzureAuthEnabled,
|
||||
grafanaCfg.UserFacingDefaultError,
|
||||
grafanaCfg.DataProxyRowLimit,
|
||||
grafanaCfg.SqlDatasourceMaxOpenConnsDefault,
|
||||
grafanaCfg.SqlDatasourceMaxIdleConnsDefault,
|
||||
grafanaCfg.SqlDatasourceMaxConnLifetimeDefault,
|
||||
), nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
class plugins.Class
|
||||
cfg *config.Cfg
|
||||
cfg *config.PluginManagementCfg
|
||||
pluginPaths []string
|
||||
want []*plugins.Plugin
|
||||
pluginErrors map[string]*plugins.SignatureError
|
||||
@@ -68,7 +68,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load a Core plugin",
|
||||
class: plugins.ClassCore,
|
||||
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
||||
cfg: &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()},
|
||||
pluginPaths: []string{filepath.Join(corePluginDir(t), "app/plugins/datasource/cloudwatch")},
|
||||
want: []*plugins.Plugin{
|
||||
{
|
||||
@@ -117,7 +117,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load a Bundled plugin",
|
||||
class: plugins.ClassBundled,
|
||||
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
||||
cfg: &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()},
|
||||
pluginPaths: []string{filepath.Join(testDataDir(t), "valid-v2-signature")},
|
||||
want: []*plugins.Plugin{
|
||||
{
|
||||
@@ -158,7 +158,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load plugin with symbolic links",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
||||
cfg: &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()},
|
||||
pluginPaths: []string{filepath.Join(testDataDir(t), "symbolic-plugin-dirs")},
|
||||
want: []*plugins.Plugin{
|
||||
{
|
||||
@@ -237,7 +237,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load an unsigned plugin (development)",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{
|
||||
cfg: &config.PluginManagementCfg{
|
||||
DevMode: true,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
@@ -277,7 +277,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load an unsigned plugin (production)",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
||||
cfg: &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()},
|
||||
pluginPaths: []string{filepath.Join(testDataDir(t), "unsigned-datasource")},
|
||||
want: []*plugins.Plugin{},
|
||||
pluginErrors: map[string]*plugins.SignatureError{
|
||||
@@ -290,7 +290,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load an unsigned plugin using PluginsAllowUnsigned config (production)",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{
|
||||
cfg: &config.PluginManagementCfg{
|
||||
PluginsAllowUnsigned: []string{"test-datasource"},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
@@ -330,7 +330,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load a plugin with v1 manifest should return signatureInvalid",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{Features: featuremgmt.WithFeatures()},
|
||||
cfg: &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()},
|
||||
pluginPaths: []string{filepath.Join(testDataDir(t), "lacking-files")},
|
||||
want: []*plugins.Plugin{},
|
||||
pluginErrors: map[string]*plugins.SignatureError{
|
||||
@@ -343,7 +343,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load a plugin with v1 manifest using PluginsAllowUnsigned config (production) should return signatureInvalid",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{
|
||||
cfg: &config.PluginManagementCfg{
|
||||
PluginsAllowUnsigned: []string{"test-datasource"},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
@@ -359,7 +359,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load a plugin with manifest which has a file not found in plugin folder",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{
|
||||
cfg: &config.PluginManagementCfg{
|
||||
PluginsAllowUnsigned: []string{"test-datasource"},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
@@ -375,7 +375,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load a plugin with file which is missing from the manifest",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{
|
||||
cfg: &config.PluginManagementCfg{
|
||||
PluginsAllowUnsigned: []string{"test-datasource"},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
@@ -391,7 +391,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load an app with includes",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{
|
||||
cfg: &config.PluginManagementCfg{
|
||||
PluginsAllowUnsigned: []string{"test-app"},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
@@ -443,7 +443,7 @@ func TestLoader_Load(t *testing.T) {
|
||||
{
|
||||
name: "Load a plugin with app sub url set",
|
||||
class: plugins.ClassExternal,
|
||||
cfg: &config.Cfg{
|
||||
cfg: &config.PluginManagementCfg{
|
||||
DevMode: true,
|
||||
GrafanaAppSubURL: "grafana",
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
@@ -511,10 +511,9 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
|
||||
stringPtr := func(s string) *string { return &s }
|
||||
|
||||
t.Run("Load a plugin with service account registration", func(t *testing.T) {
|
||||
cfg := &config.Cfg{
|
||||
cfg := &config.PluginManagementCfg{
|
||||
Features: featuremgmt.WithFeatures(featuremgmt.FlagExternalServiceAccounts),
|
||||
PluginsAllowUnsigned: []string{"grafana-test-datasource"},
|
||||
AWSAssumeRoleEnabled: true,
|
||||
}
|
||||
pluginPaths := []string{filepath.Join(testDataDir(t), "external-registration")}
|
||||
expected := []*plugins.Plugin{
|
||||
@@ -567,12 +566,6 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
|
||||
backendFactoryProvider.BackendFactoryFunc = func(ctx context.Context, plugin *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||
return func(pluginID string, logger log.Logger, env func() []string) (backendplugin.Plugin, error) {
|
||||
require.Equal(t, "grafana-test-datasource", pluginID)
|
||||
require.Equal(t, []string{
|
||||
"GF_VERSION=", "GF_EDITION=", "GF_ENTERPRISE_LICENSE_PATH=",
|
||||
"GF_ENTERPRISE_APP_URL=", "GF_ENTERPRISE_LICENSE_TEXT=", "GF_APP_URL=",
|
||||
"GF_PLUGIN_APP_CLIENT_ID=client-id", "GF_PLUGIN_APP_CLIENT_SECRET=secretz",
|
||||
"GF_INSTANCE_FEATURE_TOGGLES_ENABLE=externalServiceAccounts",
|
||||
}, env())
|
||||
return &fakes.FakeBackendPlugin{}, nil
|
||||
}
|
||||
}
|
||||
@@ -607,7 +600,7 @@ func TestLoader_Load_ExternalRegistration(t *testing.T) {
|
||||
|
||||
func TestLoader_Load_CustomSource(t *testing.T) {
|
||||
t.Run("Load a plugin", func(t *testing.T) {
|
||||
cfg := &config.Cfg{
|
||||
cfg := &config.PluginManagementCfg{
|
||||
PluginsCDNURLTemplate: "https://cdn.example.com",
|
||||
PluginSettings: setting.PluginSettings{
|
||||
"grafana-worldmap-panel": {"cdn": "true"},
|
||||
@@ -685,7 +678,7 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
|
||||
t.Run("Load multiple", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *config.Cfg
|
||||
cfg *config.PluginManagementCfg
|
||||
pluginPaths []string
|
||||
existingPlugins map[string]struct{}
|
||||
want []*plugins.Plugin
|
||||
@@ -693,7 +686,7 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "Load multiple plugins (broken, valid, unsigned)",
|
||||
cfg: &config.Cfg{
|
||||
cfg: &config.PluginManagementCfg{
|
||||
GrafanaAppURL: "http://localhost:3000",
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
@@ -783,14 +776,14 @@ func TestLoader_Load_MultiplePlugins(t *testing.T) {
|
||||
func TestLoader_Load_RBACReady(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *config.Cfg
|
||||
cfg *config.PluginManagementCfg
|
||||
pluginPaths []string
|
||||
existingPlugins map[string]struct{}
|
||||
want []*plugins.Plugin
|
||||
}{
|
||||
{
|
||||
name: "Load plugin defining one RBAC role",
|
||||
cfg: &config.Cfg{
|
||||
cfg: &config.PluginManagementCfg{
|
||||
GrafanaAppURL: "http://localhost:3000",
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
@@ -911,7 +904,7 @@ func TestLoader_Load_Signature_RootURL(t *testing.T) {
|
||||
reg := fakes.NewFakePluginRegistry()
|
||||
procPrvdr := fakes.NewFakeBackendProcessProvider()
|
||||
procMgr := fakes.NewFakeProcessManager()
|
||||
cfg := &config.Cfg{GrafanaAppURL: defaultAppURL, Features: featuremgmt.WithFeatures()}
|
||||
cfg := &config.PluginManagementCfg{GrafanaAppURL: defaultAppURL, Features: featuremgmt.WithFeatures()}
|
||||
l := newLoader(t, cfg, reg, procMgr, procPrvdr, newFakeSignatureErrorTracker())
|
||||
got, err := l.Load(context.Background(), &fakes.FakePluginSource{
|
||||
PluginClassFunc: func(ctx context.Context) plugins.Class {
|
||||
@@ -988,7 +981,7 @@ func TestLoader_Load_DuplicatePlugins(t *testing.T) {
|
||||
reg := fakes.NewFakePluginRegistry()
|
||||
procPrvdr := fakes.NewFakeBackendProcessProvider()
|
||||
procMgr := fakes.NewFakeProcessManager()
|
||||
cfg := &config.Cfg{Features: featuremgmt.WithFeatures()}
|
||||
cfg := &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()}
|
||||
l := newLoader(t, cfg, reg, procMgr, procPrvdr, newFakeSignatureErrorTracker())
|
||||
got, err := l.Load(context.Background(), &fakes.FakePluginSource{
|
||||
PluginClassFunc: func(ctx context.Context) plugins.Class {
|
||||
@@ -1078,7 +1071,7 @@ func TestLoader_Load_SkipUninitializedPlugins(t *testing.T) {
|
||||
}
|
||||
}
|
||||
procMgr := fakes.NewFakeProcessManager()
|
||||
cfg := &config.Cfg{Features: featuremgmt.WithFeatures()}
|
||||
cfg := &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()}
|
||||
l := newLoader(t, cfg, reg, procMgr, procPrvdr, newFakeSignatureErrorTracker())
|
||||
got, err := l.Load(context.Background(), &fakes.FakePluginSource{
|
||||
PluginClassFunc: func(ctx context.Context) plugins.Class {
|
||||
@@ -1135,7 +1128,7 @@ func TestLoader_AngularClass(t *testing.T) {
|
||||
},
|
||||
}
|
||||
// if angularDetected = true, it means that the detection has run
|
||||
l := newLoaderWithOpts(t, &config.Cfg{AngularSupportEnabled: true, Features: featuremgmt.WithFeatures()}, loaderDepOpts{
|
||||
l := newLoaderWithOpts(t, &config.PluginManagementCfg{AngularSupportEnabled: true, Features: featuremgmt.WithFeatures()}, loaderDepOpts{
|
||||
angularInspector: angularinspector.AlwaysAngularFakeInspector,
|
||||
})
|
||||
p, err := l.Load(context.Background(), fakePluginSource)
|
||||
@@ -1161,10 +1154,10 @@ func TestLoader_Load_Angular(t *testing.T) {
|
||||
}
|
||||
for _, cfgTc := range []struct {
|
||||
name string
|
||||
cfg *config.Cfg
|
||||
cfg *config.PluginManagementCfg
|
||||
}{
|
||||
{name: "angular support enabled", cfg: &config.Cfg{AngularSupportEnabled: true, Features: featuremgmt.WithFeatures()}},
|
||||
{name: "angular support disabled", cfg: &config.Cfg{AngularSupportEnabled: false, Features: featuremgmt.WithFeatures()}},
|
||||
{name: "angular support enabled", cfg: &config.PluginManagementCfg{AngularSupportEnabled: true, Features: featuremgmt.WithFeatures()}},
|
||||
{name: "angular support disabled", cfg: &config.PluginManagementCfg{AngularSupportEnabled: false, Features: featuremgmt.WithFeatures()}},
|
||||
} {
|
||||
t.Run(cfgTc.name, func(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
@@ -1211,20 +1204,20 @@ func TestLoader_HideAngularDeprecation(t *testing.T) {
|
||||
}
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
cfg *config.Cfg
|
||||
cfg *config.PluginManagementCfg
|
||||
expHideAngularDeprecation bool
|
||||
}{
|
||||
{name: "with plugin id in HideAngularDeprecation list", cfg: &config.Cfg{
|
||||
{name: "with plugin id in HideAngularDeprecation list", cfg: &config.PluginManagementCfg{
|
||||
AngularSupportEnabled: true,
|
||||
HideAngularDeprecation: []string{"one-app", "two-panel", "test-datasource", "three-datasource"},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
}, expHideAngularDeprecation: true},
|
||||
{name: "without plugin id in HideAngularDeprecation list", cfg: &config.Cfg{
|
||||
{name: "without plugin id in HideAngularDeprecation list", cfg: &config.PluginManagementCfg{
|
||||
AngularSupportEnabled: true,
|
||||
HideAngularDeprecation: []string{"one-app", "two-panel", "three-datasource"},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
}, expHideAngularDeprecation: false},
|
||||
{name: "with empty HideAngularDeprecation", cfg: &config.Cfg{
|
||||
{name: "with empty HideAngularDeprecation", cfg: &config.PluginManagementCfg{
|
||||
AngularSupportEnabled: true,
|
||||
HideAngularDeprecation: nil,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
@@ -1315,7 +1308,7 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
procPrvdr := fakes.NewFakeBackendProcessProvider()
|
||||
procMgr := fakes.NewFakeProcessManager()
|
||||
reg := fakes.NewFakePluginRegistry()
|
||||
cfg := &config.Cfg{Features: featuremgmt.WithFeatures()}
|
||||
cfg := &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()}
|
||||
l := newLoader(t, cfg, reg, procMgr, procPrvdr, newFakeSignatureErrorTracker())
|
||||
|
||||
got, err := l.Load(context.Background(), &fakes.FakePluginSource{
|
||||
@@ -1492,7 +1485,7 @@ func TestLoader_Load_NestedPlugins(t *testing.T) {
|
||||
reg := fakes.NewFakePluginRegistry()
|
||||
procPrvdr := fakes.NewFakeBackendProcessProvider()
|
||||
procMgr := fakes.NewFakeProcessManager()
|
||||
cfg := &config.Cfg{Features: featuremgmt.WithFeatures()}
|
||||
cfg := &config.PluginManagementCfg{Features: featuremgmt.WithFeatures()}
|
||||
l := newLoader(t, cfg, reg, procMgr, procPrvdr, newFakeSignatureErrorTracker())
|
||||
got, err := l.Load(context.Background(), &fakes.FakePluginSource{
|
||||
PluginClassFunc: func(ctx context.Context) plugins.Class {
|
||||
@@ -1523,11 +1516,10 @@ type loaderDepOpts struct {
|
||||
backendFactoryProvider plugins.BackendFactoryProvider
|
||||
}
|
||||
|
||||
func newLoader(t *testing.T, cfg *config.Cfg, reg registry.Service, proc process.Manager,
|
||||
func newLoader(t *testing.T, cfg *config.PluginManagementCfg, reg registry.Service, proc process.Manager,
|
||||
backendFactory plugins.BackendFactoryProvider, sigErrTracker pluginerrs.SignatureErrorTracker,
|
||||
) *Loader {
|
||||
assets := assetpath.ProvideService(cfg, pluginscdn.ProvideService(cfg))
|
||||
lic := fakes.NewFakeLicensingService()
|
||||
angularInspector := angularinspector.NewStaticInspector()
|
||||
|
||||
terminate, err := pipeline.ProvideTerminationStage(cfg, reg, proc)
|
||||
@@ -1537,13 +1529,12 @@ func newLoader(t *testing.T, cfg *config.Cfg, reg registry.Service, proc process
|
||||
finder.NewLocalFinder(false, featuremgmt.WithFeatures()), reg),
|
||||
pipeline.ProvideBootstrapStage(cfg, signature.DefaultCalculator(cfg), assets),
|
||||
pipeline.ProvideValidationStage(cfg, signature.NewValidator(signature.NewUnsignedAuthorizer(cfg)), angularInspector, sigErrTracker),
|
||||
pipeline.ProvideInitializationStage(cfg, reg, lic, backendFactory, proc, &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry()),
|
||||
pipeline.ProvideInitializationStage(cfg, reg, backendFactory, proc, &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry(), fakes.NewFakePluginEnvProvider()),
|
||||
terminate)
|
||||
}
|
||||
|
||||
func newLoaderWithOpts(t *testing.T, cfg *config.Cfg, opts loaderDepOpts) *Loader {
|
||||
func newLoaderWithOpts(t *testing.T, cfg *config.PluginManagementCfg, opts loaderDepOpts) *Loader {
|
||||
assets := assetpath.ProvideService(cfg, pluginscdn.ProvideService(cfg))
|
||||
lic := fakes.NewFakeLicensingService()
|
||||
reg := fakes.NewFakePluginRegistry()
|
||||
proc := fakes.NewFakeProcessManager()
|
||||
|
||||
@@ -1570,7 +1561,7 @@ func newLoaderWithOpts(t *testing.T, cfg *config.Cfg, opts loaderDepOpts) *Loade
|
||||
finder.NewLocalFinder(false, featuremgmt.WithFeatures()), reg),
|
||||
pipeline.ProvideBootstrapStage(cfg, signature.DefaultCalculator(cfg), assets),
|
||||
pipeline.ProvideValidationStage(cfg, signature.NewValidator(signature.NewUnsignedAuthorizer(cfg)), angularInspector, sigErrTracker),
|
||||
pipeline.ProvideInitializationStage(cfg, reg, lic, backendFactoryProvider, proc, authServiceRegistry, fakes.NewFakeRoleRegistry()),
|
||||
pipeline.ProvideInitializationStage(cfg, reg, backendFactoryProvider, proc, authServiceRegistry, fakes.NewFakeRoleRegistry(), fakes.NewFakePluginEnvProvider()),
|
||||
terminate)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs"
|
||||
)
|
||||
|
||||
func ProvideDiscoveryStage(cfg *config.Cfg, pf finder.Finder, pr registry.Service) *discovery.Discovery {
|
||||
func ProvideDiscoveryStage(cfg *config.PluginManagementCfg, pf finder.Finder, pr registry.Service) *discovery.Discovery {
|
||||
return discovery.New(cfg, discovery.Opts{
|
||||
FindFunc: pf.Find,
|
||||
FindFilterFuncs: []discovery.FindFilterFunc{
|
||||
@@ -41,14 +41,14 @@ func ProvideDiscoveryStage(cfg *config.Cfg, pf finder.Finder, pr registry.Servic
|
||||
})
|
||||
}
|
||||
|
||||
func ProvideBootstrapStage(cfg *config.Cfg, sc plugins.SignatureCalculator, a *assetpath.Service) *bootstrap.Bootstrap {
|
||||
func ProvideBootstrapStage(cfg *config.PluginManagementCfg, sc plugins.SignatureCalculator, a *assetpath.Service) *bootstrap.Bootstrap {
|
||||
return bootstrap.New(cfg, bootstrap.Opts{
|
||||
ConstructFunc: bootstrap.DefaultConstructFunc(sc, a),
|
||||
DecorateFuncs: bootstrap.DefaultDecorateFuncs(cfg),
|
||||
})
|
||||
}
|
||||
|
||||
func ProvideValidationStage(cfg *config.Cfg, sv signature.Validator, ai angularinspector.Inspector,
|
||||
func ProvideValidationStage(cfg *config.PluginManagementCfg, sv signature.Validator, ai angularinspector.Inspector,
|
||||
et pluginerrs.SignatureErrorTracker) *validation.Validate {
|
||||
return validation.New(cfg, validation.Opts{
|
||||
ValidateFuncs: []validation.ValidateFunc{
|
||||
@@ -59,13 +59,13 @@ func ProvideValidationStage(cfg *config.Cfg, sv signature.Validator, ai angulari
|
||||
})
|
||||
}
|
||||
|
||||
func ProvideInitializationStage(cfg *config.Cfg, pr registry.Service, l plugins.Licensing,
|
||||
bp plugins.BackendFactoryProvider, pm process.Manager, externalServiceRegistry auth.ExternalServiceRegistry,
|
||||
roleRegistry plugins.RoleRegistry) *initialization.Initialize {
|
||||
func ProvideInitializationStage(cfg *config.PluginManagementCfg, pr registry.Service, bp plugins.BackendFactoryProvider,
|
||||
pm process.Manager, externalServiceRegistry auth.ExternalServiceRegistry,
|
||||
roleRegistry plugins.RoleRegistry, pluginEnvProvider envvars.Provider) *initialization.Initialize {
|
||||
return initialization.New(cfg, initialization.Opts{
|
||||
InitializeFuncs: []initialization.InitializeFunc{
|
||||
ExternalServiceRegistrationStep(cfg, externalServiceRegistry),
|
||||
initialization.BackendClientInitStep(envvars.NewProvider(cfg, l), bp),
|
||||
initialization.BackendClientInitStep(pluginEnvProvider, bp),
|
||||
initialization.PluginRegistrationStep(pr),
|
||||
initialization.BackendProcessStartStep(pm),
|
||||
RegisterPluginRolesStep(roleRegistry),
|
||||
@@ -74,7 +74,7 @@ func ProvideInitializationStage(cfg *config.Cfg, pr registry.Service, l plugins.
|
||||
})
|
||||
}
|
||||
|
||||
func ProvideTerminationStage(cfg *config.Cfg, pr registry.Service, pm process.Manager) (*termination.Terminate, error) {
|
||||
func ProvideTerminationStage(cfg *config.PluginManagementCfg, pr registry.Service, pm process.Manager) (*termination.Terminate, error) {
|
||||
return termination.New(cfg, termination.Opts{
|
||||
TerminateFuncs: []termination.TerminateFunc{
|
||||
termination.BackendProcessTerminatorStep(pm),
|
||||
|
||||
@@ -21,17 +21,17 @@ import (
|
||||
|
||||
// ExternalServiceRegistration implements an InitializeFunc for registering external services.
|
||||
type ExternalServiceRegistration struct {
|
||||
cfg *config.Cfg
|
||||
cfg *config.PluginManagementCfg
|
||||
externalServiceRegistry auth.ExternalServiceRegistry
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
// ExternalServiceRegistrationStep returns an InitializeFunc for registering external services.
|
||||
func ExternalServiceRegistrationStep(cfg *config.Cfg, externalServiceRegistry auth.ExternalServiceRegistry) initialization.InitializeFunc {
|
||||
func ExternalServiceRegistrationStep(cfg *config.PluginManagementCfg, externalServiceRegistry auth.ExternalServiceRegistry) initialization.InitializeFunc {
|
||||
return newExternalServiceRegistration(cfg, externalServiceRegistry).Register
|
||||
}
|
||||
|
||||
func newExternalServiceRegistration(cfg *config.Cfg, serviceRegistry auth.ExternalServiceRegistry) *ExternalServiceRegistration {
|
||||
func newExternalServiceRegistration(cfg *config.PluginManagementCfg, serviceRegistry auth.ExternalServiceRegistry) *ExternalServiceRegistration {
|
||||
return &ExternalServiceRegistration{
|
||||
cfg: cfg,
|
||||
externalServiceRegistry: serviceRegistry,
|
||||
@@ -128,11 +128,11 @@ func (v *SignatureValidation) Validate(ctx context.Context, p *plugins.Plugin) e
|
||||
// DisablePlugins is a filter step that will filter out any configured plugins
|
||||
type DisablePlugins struct {
|
||||
log log.Logger
|
||||
cfg *config.Cfg
|
||||
cfg *config.PluginManagementCfg
|
||||
}
|
||||
|
||||
// NewDisablePluginsStep returns a new DisablePlugins.
|
||||
func NewDisablePluginsStep(cfg *config.Cfg) *DisablePlugins {
|
||||
func NewDisablePluginsStep(cfg *config.PluginManagementCfg) *DisablePlugins {
|
||||
return &DisablePlugins{
|
||||
cfg: cfg,
|
||||
log: log.New("plugins.disable"),
|
||||
@@ -164,11 +164,11 @@ func (c *DisablePlugins) Filter(bundles []*plugins.FoundBundle) ([]*plugins.Foun
|
||||
// AsExternal is a filter step that will skip loading a core plugin to use an external one.
|
||||
type AsExternal struct {
|
||||
log log.Logger
|
||||
cfg *config.Cfg
|
||||
cfg *config.PluginManagementCfg
|
||||
}
|
||||
|
||||
// NewDisablePluginsStep returns a new DisablePlugins.
|
||||
func NewAsExternalStep(cfg *config.Cfg) *AsExternal {
|
||||
func NewAsExternalStep(cfg *config.PluginManagementCfg) *AsExternal {
|
||||
return &AsExternal{
|
||||
cfg: cfg,
|
||||
log: log.New("plugins.asExternal"),
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func TestSkipPlugins(t *testing.T) {
|
||||
cfg := &config.Cfg{
|
||||
cfg := &config.PluginManagementCfg{
|
||||
DisablePlugins: []string{"plugin1", "plugin2"},
|
||||
}
|
||||
s := NewDisablePluginsStep(cfg)
|
||||
@@ -68,7 +68,7 @@ func TestAsExternal(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("should skip a core plugin", func(t *testing.T) {
|
||||
cfg := &config.Cfg{
|
||||
cfg := &config.PluginManagementCfg{
|
||||
Features: featuremgmt.WithFeatures(featuremgmt.FlagExternalCorePlugins),
|
||||
PluginSettings: setting.PluginSettings{
|
||||
"plugin1": map[string]string{
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package pluginconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
|
||||
"github.com/grafana/grafana-azure-sdk-go/azsettings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
// ProvidePluginManagementConfig returns a new config.PluginManagementCfg.
|
||||
// It is used to provide configuration to Grafana's implementation of the plugin management system.
|
||||
func ProvidePluginManagementConfig(cfg *setting.Cfg, settingProvider setting.Provider, features featuremgmt.FeatureToggles) (*config.PluginManagementCfg, error) {
|
||||
plugins := settingProvider.Section("plugins")
|
||||
allowedUnsigned := cfg.PluginsAllowUnsigned
|
||||
if len(plugins.KeyValue("allow_loading_unsigned_plugins").Value()) > 0 {
|
||||
allowedUnsigned = strings.Split(plugins.KeyValue("allow_loading_unsigned_plugins").Value(), ",")
|
||||
}
|
||||
|
||||
tracingCfg, err := newTracingCfg(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new opentelemetry cfg: %w", err)
|
||||
}
|
||||
|
||||
return config.NewPluginManagementCfg(
|
||||
settingProvider.KeyValue("", "app_mode").MustBool(cfg.Env == setting.Dev),
|
||||
cfg.PluginsPath,
|
||||
extractPluginSettings(settingProvider),
|
||||
allowedUnsigned,
|
||||
cfg.PluginsCDNURLTemplate,
|
||||
cfg.AppURL,
|
||||
cfg.AppSubURL,
|
||||
tracingCfg,
|
||||
features,
|
||||
cfg.AngularSupportEnabled,
|
||||
cfg.GrafanaComURL,
|
||||
cfg.DisablePlugins,
|
||||
cfg.HideAngularDeprecation,
|
||||
cfg.ForwardHostEnvVars,
|
||||
), nil
|
||||
}
|
||||
|
||||
// PluginInstanceCfg contains the configuration for a plugin instance.
|
||||
// It is used to provide configuration to the plugin instance either via env vars or via each plugin request.
|
||||
type PluginInstanceCfg struct {
|
||||
GrafanaAppURL string
|
||||
Features featuremgmt.FeatureToggles
|
||||
|
||||
Tracing config.Tracing
|
||||
|
||||
PluginSettings setting.PluginSettings
|
||||
|
||||
AWSAllowedAuthProviders []string
|
||||
AWSAssumeRoleEnabled bool
|
||||
AWSExternalId string
|
||||
AWSSessionDuration string
|
||||
AWSListMetricsPageLimit string
|
||||
AWSForwardSettingsPlugins []string
|
||||
|
||||
Azure *azsettings.AzureSettings
|
||||
AzureAuthEnabled bool
|
||||
|
||||
ProxySettings setting.SecureSocksDSProxySettings
|
||||
|
||||
GrafanaVersion string
|
||||
|
||||
ConcurrentQueryCount int
|
||||
|
||||
UserFacingDefaultError string
|
||||
|
||||
DataProxyRowLimit int64
|
||||
|
||||
SQLDatasourceMaxOpenConnsDefault int
|
||||
SQLDatasourceMaxIdleConnsDefault int
|
||||
SQLDatasourceMaxConnLifetimeDefault int
|
||||
}
|
||||
|
||||
// ProvidePluginInstanceConfig returns a new PluginInstanceCfg.
|
||||
func ProvidePluginInstanceConfig(cfg *setting.Cfg, settingProvider setting.Provider, features featuremgmt.FeatureToggles) (*PluginInstanceCfg, error) {
|
||||
aws := settingProvider.Section("aws")
|
||||
allowedAuth := cfg.AWSAllowedAuthProviders
|
||||
if len(aws.KeyValue("allowed_auth_providers").Value()) > 0 {
|
||||
allowedAuth = util.SplitString(aws.KeyValue("allowed_auth_providers").Value())
|
||||
}
|
||||
awsForwardSettingsPlugins := cfg.AWSForwardSettingsPlugins
|
||||
if len(aws.KeyValue("forward_settings_to_plugins").Value()) > 0 {
|
||||
awsForwardSettingsPlugins = util.SplitString(aws.KeyValue("forward_settings_to_plugins").Value())
|
||||
}
|
||||
|
||||
tracingCfg, err := newTracingCfg(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("new opentelemetry cfg: %w", err)
|
||||
}
|
||||
|
||||
if cfg.Azure == nil {
|
||||
cfg.Azure = &azsettings.AzureSettings{}
|
||||
}
|
||||
|
||||
return &PluginInstanceCfg{
|
||||
GrafanaAppURL: cfg.AppURL,
|
||||
Features: features,
|
||||
Tracing: tracingCfg,
|
||||
PluginSettings: extractPluginSettings(settingProvider),
|
||||
AWSAllowedAuthProviders: allowedAuth,
|
||||
AWSAssumeRoleEnabled: aws.KeyValue("assume_role_enabled").MustBool(cfg.AWSAssumeRoleEnabled),
|
||||
AWSExternalId: aws.KeyValue("external_id").Value(),
|
||||
AWSSessionDuration: aws.KeyValue("session_duration").Value(),
|
||||
AWSListMetricsPageLimit: aws.KeyValue("list_metrics_page_limit").Value(),
|
||||
AWSForwardSettingsPlugins: awsForwardSettingsPlugins,
|
||||
Azure: cfg.Azure,
|
||||
AzureAuthEnabled: cfg.Azure.AzureAuthEnabled,
|
||||
ProxySettings: cfg.SecureSocksDSProxy,
|
||||
GrafanaVersion: cfg.BuildVersion,
|
||||
ConcurrentQueryCount: cfg.ConcurrentQueryCount,
|
||||
UserFacingDefaultError: cfg.UserFacingDefaultError,
|
||||
DataProxyRowLimit: cfg.DataProxyRowLimit,
|
||||
SQLDatasourceMaxOpenConnsDefault: cfg.SqlDatasourceMaxOpenConnsDefault,
|
||||
SQLDatasourceMaxIdleConnsDefault: cfg.SqlDatasourceMaxIdleConnsDefault,
|
||||
SQLDatasourceMaxConnLifetimeDefault: cfg.SqlDatasourceMaxConnLifetimeDefault,
|
||||
}, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package config
|
||||
package pluginconfig
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -18,7 +18,7 @@ func TestPluginSettings(t *testing.T) {
|
||||
[plugin.test-datasource]
|
||||
foo = 5m
|
||||
bar = something
|
||||
|
||||
|
||||
[plugin.secret-plugin]
|
||||
secret_key = secret
|
||||
normal_key = not a secret`))
|
||||
@@ -0,0 +1,178 @@
|
||||
package pluginconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
|
||||
"github.com/grafana/grafana-azure-sdk-go/azsettings"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/proxy"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/envvars"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
)
|
||||
|
||||
var _ envvars.Provider = (*EnvVarsProvider)(nil)
|
||||
|
||||
type EnvVarsProvider struct {
|
||||
cfg *PluginInstanceCfg
|
||||
license plugins.Licensing
|
||||
}
|
||||
|
||||
func NewEnvVarsProvider(cfg *PluginInstanceCfg, license plugins.Licensing) *EnvVarsProvider {
|
||||
return &EnvVarsProvider{
|
||||
cfg: cfg,
|
||||
license: license,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *EnvVarsProvider) PluginEnvVars(ctx context.Context, plugin *plugins.Plugin) []string {
|
||||
hostEnv := []string{
|
||||
envVar("GF_VERSION", p.cfg.GrafanaVersion),
|
||||
}
|
||||
|
||||
if p.license != nil {
|
||||
hostEnv = append(
|
||||
hostEnv,
|
||||
envVar("GF_EDITION", p.license.Edition()),
|
||||
envVar("GF_ENTERPRISE_LICENSE_PATH", p.license.Path()),
|
||||
envVar("GF_ENTERPRISE_APP_URL", p.license.AppURL()),
|
||||
)
|
||||
hostEnv = append(hostEnv, p.license.Environment()...)
|
||||
}
|
||||
|
||||
if plugin.ExternalService != nil {
|
||||
hostEnv = append(
|
||||
hostEnv,
|
||||
envVar("GF_APP_URL", p.cfg.GrafanaAppURL),
|
||||
envVar("GF_PLUGIN_APP_CLIENT_ID", plugin.ExternalService.ClientID),
|
||||
envVar("GF_PLUGIN_APP_CLIENT_SECRET", plugin.ExternalService.ClientSecret),
|
||||
)
|
||||
if plugin.ExternalService.PrivateKey != "" {
|
||||
hostEnv = append(hostEnv, envVar("GF_PLUGIN_APP_PRIVATE_KEY", plugin.ExternalService.PrivateKey))
|
||||
}
|
||||
}
|
||||
|
||||
hostEnv = append(hostEnv, p.featureToggleEnableVars(ctx)...)
|
||||
hostEnv = append(hostEnv, p.awsEnvVars(plugin.PluginID())...)
|
||||
hostEnv = append(hostEnv, p.secureSocksProxyEnvVars()...)
|
||||
hostEnv = append(hostEnv, azsettings.WriteToEnvStr(p.cfg.Azure)...)
|
||||
hostEnv = append(hostEnv, p.tracingEnvVars(plugin)...)
|
||||
hostEnv = append(hostEnv, p.pluginSettingsEnvVars(plugin.PluginID())...)
|
||||
|
||||
// If SkipHostEnvVars is enabled, get some allowed variables from the current process and pass
|
||||
// them down to the plugin. If the flag is not set, do not add anything else because ALL env vars
|
||||
// from the current process (os.Environ()) will be forwarded to the plugin's process by go-plugin
|
||||
if plugin.SkipHostEnvVars {
|
||||
hostEnv = append(hostEnv, envvars.PermittedHostEnvVars()...)
|
||||
}
|
||||
|
||||
return hostEnv
|
||||
}
|
||||
|
||||
func (p *EnvVarsProvider) featureToggleEnableVars(ctx context.Context) []string {
|
||||
var variables []string // an array is used for consistency and keep the logic simpler for no features case
|
||||
|
||||
if p.cfg.Features == nil {
|
||||
return variables
|
||||
}
|
||||
|
||||
enabledFeatures := p.cfg.Features.GetEnabled(ctx)
|
||||
if len(enabledFeatures) > 0 {
|
||||
features := make([]string, 0, len(enabledFeatures))
|
||||
for feat := range enabledFeatures {
|
||||
features = append(features, feat)
|
||||
}
|
||||
variables = append(variables, envVar("GF_INSTANCE_FEATURE_TOGGLES_ENABLE", strings.Join(features, ",")))
|
||||
}
|
||||
|
||||
return variables
|
||||
}
|
||||
|
||||
func (p *EnvVarsProvider) awsEnvVars(pluginID string) []string {
|
||||
if !slices.Contains[[]string, string](p.cfg.AWSForwardSettingsPlugins, pluginID) {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
var variables []string
|
||||
if !p.cfg.AWSAssumeRoleEnabled {
|
||||
variables = append(variables, envVar(awsds.AssumeRoleEnabledEnvVarKeyName, "false"))
|
||||
}
|
||||
if len(p.cfg.AWSAllowedAuthProviders) > 0 {
|
||||
variables = append(variables, envVar(awsds.AllowedAuthProvidersEnvVarKeyName, strings.Join(p.cfg.AWSAllowedAuthProviders, ",")))
|
||||
}
|
||||
if p.cfg.AWSExternalId != "" {
|
||||
variables = append(variables, envVar(awsds.GrafanaAssumeRoleExternalIdKeyName, p.cfg.AWSExternalId))
|
||||
}
|
||||
if p.cfg.AWSSessionDuration != "" {
|
||||
variables = append(variables, envVar(awsds.SessionDurationEnvVarKeyName, p.cfg.AWSSessionDuration))
|
||||
}
|
||||
if p.cfg.AWSListMetricsPageLimit != "" {
|
||||
variables = append(variables, envVar(awsds.ListMetricsPageLimitKeyName, p.cfg.AWSListMetricsPageLimit))
|
||||
}
|
||||
|
||||
return variables
|
||||
}
|
||||
|
||||
func (p *EnvVarsProvider) secureSocksProxyEnvVars() []string {
|
||||
if p.cfg.ProxySettings.Enabled {
|
||||
return []string{
|
||||
envVar(proxy.PluginSecureSocksProxyClientCert, p.cfg.ProxySettings.ClientCert),
|
||||
envVar(proxy.PluginSecureSocksProxyClientKey, p.cfg.ProxySettings.ClientKey),
|
||||
envVar(proxy.PluginSecureSocksProxyRootCACert, p.cfg.ProxySettings.RootCA),
|
||||
envVar(proxy.PluginSecureSocksProxyProxyAddress, p.cfg.ProxySettings.ProxyAddress),
|
||||
envVar(proxy.PluginSecureSocksProxyServerName, p.cfg.ProxySettings.ServerName),
|
||||
envVar(proxy.PluginSecureSocksProxyEnabled, strconv.FormatBool(p.cfg.ProxySettings.Enabled)),
|
||||
envVar(proxy.PluginSecureSocksProxyAllowInsecure, strconv.FormatBool(p.cfg.ProxySettings.AllowInsecure)),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *EnvVarsProvider) tracingEnvVars(plugin *plugins.Plugin) []string {
|
||||
pluginTracingEnabled := p.cfg.Features.IsEnabledGlobally(featuremgmt.FlagEnablePluginsTracingByDefault)
|
||||
if v, exists := p.cfg.PluginSettings[plugin.ID]["tracing"]; exists && !pluginTracingEnabled {
|
||||
pluginTracingEnabled = v == "true"
|
||||
}
|
||||
|
||||
if !p.cfg.Tracing.IsEnabled() || !pluginTracingEnabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
vars := []string{
|
||||
envVar("GF_INSTANCE_OTLP_ADDRESS", p.cfg.Tracing.OpenTelemetry.Address),
|
||||
envVar("GF_INSTANCE_OTLP_PROPAGATION", p.cfg.Tracing.OpenTelemetry.Propagation),
|
||||
envVar("GF_INSTANCE_OTLP_SAMPLER_TYPE", p.cfg.Tracing.OpenTelemetry.Sampler),
|
||||
fmt.Sprintf("GF_INSTANCE_OTLP_SAMPLER_PARAM=%.6f", p.cfg.Tracing.OpenTelemetry.SamplerParam),
|
||||
envVar("GF_INSTANCE_OTLP_SAMPLER_REMOTE_URL", p.cfg.Tracing.OpenTelemetry.SamplerRemoteURL),
|
||||
}
|
||||
if plugin.Info.Version != "" {
|
||||
vars = append(vars, fmt.Sprintf("GF_PLUGIN_VERSION=%s", plugin.Info.Version))
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
func (p *EnvVarsProvider) pluginSettingsEnvVars(pluginID string) []string {
|
||||
const customConfigPrefix = "GF_PLUGIN"
|
||||
var env []string
|
||||
for k, v := range p.cfg.PluginSettings[pluginID] {
|
||||
if k == "path" || strings.ToLower(k) == "id" {
|
||||
continue
|
||||
}
|
||||
key := fmt.Sprintf("%s_%s", customConfigPrefix, strings.ToUpper(k))
|
||||
if value := os.Getenv(key); value != "" {
|
||||
v = value
|
||||
}
|
||||
env = append(env, fmt.Sprintf("%s=%s", key, v))
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
func envVar(key, value string) string {
|
||||
return fmt.Sprintf("%s=%s", key, value)
|
||||
}
|
||||
@@ -0,0 +1,742 @@
|
||||
package pluginconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana-azure-sdk-go/azsettings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/auth"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/plugins/envvars"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
|
||||
"github.com/grafana/grafana/pkg/plugins/plugindef"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestPluginEnvVarsProvider_PluginEnvVars(t *testing.T) {
|
||||
t.Run("backend datasource with license", func(t *testing.T) {
|
||||
p := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test",
|
||||
},
|
||||
}
|
||||
|
||||
licensing := &fakes.FakeLicensingService{
|
||||
LicenseEdition: "test",
|
||||
TokenRaw: "token",
|
||||
LicensePath: "/path/to/ent/license",
|
||||
LicenseAppURL: "https://myorg.com/",
|
||||
}
|
||||
|
||||
cfg := &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{
|
||||
"test": {
|
||||
"custom_env_var": "customVal",
|
||||
},
|
||||
},
|
||||
AWSAssumeRoleEnabled: true,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
provider := NewEnvVarsProvider(cfg, licensing)
|
||||
envVars := provider.PluginEnvVars(context.Background(), p)
|
||||
assert.Len(t, envVars, 6)
|
||||
assert.Equal(t, "GF_VERSION=", envVars[0])
|
||||
assert.Equal(t, "GF_EDITION=test", envVars[1])
|
||||
assert.Equal(t, "GF_ENTERPRISE_LICENSE_PATH=/path/to/ent/license", envVars[2])
|
||||
assert.Equal(t, "GF_ENTERPRISE_APP_URL=https://myorg.com/", envVars[3])
|
||||
assert.Equal(t, "GF_ENTERPRISE_LICENSE_TEXT=token", envVars[4])
|
||||
assert.Equal(t, "GF_PLUGIN_CUSTOM_ENV_VAR=customVal", envVars[5])
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginEnvVarsProvider_skipHostEnvVars(t *testing.T) {
|
||||
const (
|
||||
envVarName = "HTTP_PROXY"
|
||||
envVarValue = "lorem ipsum"
|
||||
)
|
||||
|
||||
t.Setenv(envVarName, envVarValue)
|
||||
|
||||
p := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test",
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("without FlagPluginsSkipHostEnvVars should not populate host env vars", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := NewEnvVarsProvider(pCfg, nil)
|
||||
envVars := provider.PluginEnvVars(context.Background(), p)
|
||||
|
||||
// We want to test that the envvars.Provider does not add any of the host env vars.
|
||||
// When starting the plugin via go-plugin, ALL host env vars will be added by go-plugin,
|
||||
// but we are testing the envvars.Provider here, so that's outside the scope of this test.
|
||||
_, ok := getEnvVarWithExists(envVars, envVarName)
|
||||
require.False(t, ok, "host env var should not be present")
|
||||
})
|
||||
|
||||
t.Run("with SkipHostEnvVars = true", func(t *testing.T) {
|
||||
p.SkipHostEnvVars = true
|
||||
|
||||
cfg := setting.NewCfg()
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
provider := NewEnvVarsProvider(pCfg, nil)
|
||||
|
||||
t.Run("should populate allowed host env vars", func(t *testing.T) {
|
||||
// Set all allowed variables
|
||||
for _, ev := range envvars.PermittedHostEnvVarNames() {
|
||||
t.Setenv(ev, envVarValue)
|
||||
}
|
||||
envVars := provider.PluginEnvVars(context.Background(), p)
|
||||
|
||||
// Test against each variable
|
||||
for _, expEvName := range envvars.PermittedHostEnvVarNames() {
|
||||
gotEvValue, ok := getEnvVarWithExists(envVars, expEvName)
|
||||
require.True(t, ok, "host env var should be present")
|
||||
require.Equal(t, envVarValue, gotEvValue)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should not populate host env vars that aren't allowed", func(t *testing.T) {
|
||||
// Set all allowed variables
|
||||
for _, ev := range envvars.PermittedHostEnvVarNames() {
|
||||
t.Setenv(ev, envVarValue)
|
||||
}
|
||||
// ...and an extra one, which should not leak
|
||||
const superSecretEnvVariableName = "SUPER_SECRET_VALUE"
|
||||
t.Setenv(superSecretEnvVariableName, "01189998819991197253")
|
||||
envVars := provider.PluginEnvVars(context.Background(), p)
|
||||
|
||||
// Super secret should not leak
|
||||
_, ok := getEnvVarWithExists(envVars, superSecretEnvVariableName)
|
||||
require.False(t, ok, "super secret env var should not be leaked")
|
||||
|
||||
// Everything else should be present
|
||||
for _, expEvName := range envvars.PermittedHostEnvVarNames() {
|
||||
var gotEvValue string
|
||||
gotEvValue, ok = getEnvVarWithExists(envVars, expEvName)
|
||||
require.True(t, ok, "host env var should be present")
|
||||
require.Equal(t, envVarValue, gotEvValue)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginEnvVarsProvider_tracingEnvironmentVariables(t *testing.T) {
|
||||
const pluginID = "plugin_id"
|
||||
|
||||
defaultPlugin := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: pluginID,
|
||||
Info: plugins.Info{Version: "1.0.0"},
|
||||
},
|
||||
}
|
||||
pluginWithoutVersion := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{ID: pluginID},
|
||||
}
|
||||
|
||||
defaultOTelCfg := config.OpenTelemetryCfg{
|
||||
Address: "127.0.0.1:4317",
|
||||
Propagation: "",
|
||||
}
|
||||
|
||||
expDefaultOtlp := func(t *testing.T, envVars []string) {
|
||||
found := map[string]bool{
|
||||
"address": false,
|
||||
"plugin_version": false,
|
||||
"propagation": false,
|
||||
}
|
||||
setFound := func(v string) {
|
||||
require.False(t, found[v], "duplicate env var found")
|
||||
found[v] = true
|
||||
}
|
||||
for _, v := range envVars {
|
||||
switch v {
|
||||
case "GF_PLUGIN_VERSION=1.0.0":
|
||||
setFound("plugin_version")
|
||||
case "GF_INSTANCE_OTLP_ADDRESS=127.0.0.1:4317":
|
||||
setFound("address")
|
||||
case "GF_INSTANCE_OTLP_PROPAGATION=":
|
||||
setFound("propagation")
|
||||
}
|
||||
}
|
||||
for k, f := range found {
|
||||
require.Truef(t, f, "%q env var not found: %+v", k, envVars)
|
||||
}
|
||||
}
|
||||
expNoTracing := func(t *testing.T, envVars []string) {
|
||||
for _, v := range envVars {
|
||||
assert.False(t, strings.HasPrefix(v, "GF_TRACING"), "should not have tracing env var")
|
||||
assert.False(
|
||||
t,
|
||||
strings.HasPrefix(v, "GF_PLUGIN_VERSION"),
|
||||
"GF_PLUGIN_VERSION is tracing-only and should not be present when tracing is disabled",
|
||||
)
|
||||
}
|
||||
}
|
||||
expGfPluginVersionNotPresent := func(t *testing.T, envVars []string) {
|
||||
for _, e := range envVars {
|
||||
assert.False(t, strings.HasPrefix("GF_PLUGIN_VERSION=", e), "GF_PLUGIN_VERSION shouldn't be present")
|
||||
}
|
||||
}
|
||||
expGfPluginVersionPresent := func(t *testing.T, envVars []string) {
|
||||
var found bool
|
||||
for _, e := range envVars {
|
||||
if e != "GF_PLUGIN_VERSION=1.0.0" {
|
||||
continue
|
||||
}
|
||||
assert.False(t, found, "GF_PLUGIN_VERSION is present multiple times")
|
||||
found = true
|
||||
}
|
||||
assert.Truef(t, found, "GF_PLUGIN_VERSION is not present: %+v", envVars)
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
cfg *PluginInstanceCfg
|
||||
plugin *plugins.Plugin
|
||||
exp func(t *testing.T, envVars []string)
|
||||
}{
|
||||
{
|
||||
name: "otel not configured",
|
||||
cfg: &PluginInstanceCfg{
|
||||
AWSAssumeRoleEnabled: false,
|
||||
Tracing: config.Tracing{},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expNoTracing,
|
||||
},
|
||||
{
|
||||
name: "otel not configured but plugin-tracing enabled",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}},
|
||||
Tracing: config.Tracing{},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expNoTracing,
|
||||
},
|
||||
{
|
||||
name: "otlp no propagation plugin enabled",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{
|
||||
pluginID: {"tracing": "true"},
|
||||
},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: defaultOTelCfg,
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
|
||||
plugin: defaultPlugin,
|
||||
exp: expDefaultOtlp,
|
||||
},
|
||||
{
|
||||
name: "otlp no propagation disabled by default",
|
||||
cfg: &PluginInstanceCfg{
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: defaultOTelCfg,
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expNoTracing,
|
||||
},
|
||||
{
|
||||
name: "otlp propagation plugin enabled",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{
|
||||
pluginID: {"tracing": "true"},
|
||||
},
|
||||
AWSAssumeRoleEnabled: true,
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{
|
||||
Address: "127.0.0.1:4317",
|
||||
Propagation: "w3c",
|
||||
|
||||
// Sensible default values for the sampler set by pkg/infra/tracing while reading config.ini
|
||||
Sampler: "",
|
||||
SamplerParam: 1.0,
|
||||
SamplerRemoteURL: "",
|
||||
},
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: func(t *testing.T, envVars []string) {
|
||||
assert.Len(t, envVars, 8)
|
||||
assert.Equal(t, "GF_VERSION=", envVars[0])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_ADDRESS=127.0.0.1:4317", envVars[1])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_PROPAGATION=w3c", envVars[2])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_SAMPLER_TYPE=", envVars[3])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_SAMPLER_PARAM=1.000000", envVars[4])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_SAMPLER_REMOTE_URL=", envVars[5])
|
||||
assert.Equal(t, "GF_PLUGIN_VERSION=1.0.0", envVars[6])
|
||||
assert.Equal(t, "GF_PLUGIN_TRACING=true", envVars[7])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "otlp enabled composite propagation",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{
|
||||
pluginID: {"tracing": "true"},
|
||||
},
|
||||
AWSAssumeRoleEnabled: true,
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{
|
||||
Address: "127.0.0.1:4317",
|
||||
Propagation: "w3c,jaeger",
|
||||
|
||||
// Sensible default values for the sampler set by pkg/infra/tracing while reading config.ini
|
||||
Sampler: "",
|
||||
SamplerParam: 1.0,
|
||||
SamplerRemoteURL: "",
|
||||
},
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: func(t *testing.T, envVars []string) {
|
||||
assert.Len(t, envVars, 8)
|
||||
assert.Equal(t, "GF_VERSION=", envVars[0])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_ADDRESS=127.0.0.1:4317", envVars[1])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_PROPAGATION=w3c,jaeger", envVars[2])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_SAMPLER_TYPE=", envVars[3])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_SAMPLER_PARAM=1.000000", envVars[4])
|
||||
assert.Equal(t, "GF_INSTANCE_OTLP_SAMPLER_REMOTE_URL=", envVars[5])
|
||||
assert.Equal(t, "GF_PLUGIN_VERSION=1.0.0", envVars[6])
|
||||
assert.Equal(t, "GF_PLUGIN_TRACING=true", envVars[7])
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "otlp no propagation disabled by default",
|
||||
cfg: &PluginInstanceCfg{
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{
|
||||
Address: "127.0.0.1:4317",
|
||||
Propagation: "w3c",
|
||||
},
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expNoTracing,
|
||||
},
|
||||
{
|
||||
name: "disabled on plugin",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: setting.PluginSettings{
|
||||
pluginID: map[string]string{"tracing": "false"},
|
||||
},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: defaultOTelCfg,
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expNoTracing,
|
||||
},
|
||||
{
|
||||
name: "disabled on plugin with other plugin settings",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{
|
||||
pluginID: {"some_other_option": "true"},
|
||||
},
|
||||
AWSAssumeRoleEnabled: true,
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: defaultOTelCfg,
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expNoTracing,
|
||||
},
|
||||
{
|
||||
name: "enabled on plugin with other plugin settings",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{
|
||||
pluginID: {"some_other_option": "true", "tracing": "true"},
|
||||
},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: defaultOTelCfg,
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expDefaultOtlp,
|
||||
},
|
||||
{
|
||||
name: `enabled on plugin with no "tracing" plugin setting but with enablePluginsTracingByDefault feature flag`,
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {}},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: defaultOTelCfg,
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(featuremgmt.FlagEnablePluginsTracingByDefault),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expDefaultOtlp,
|
||||
},
|
||||
{
|
||||
name: `enabled on plugin with plugin setting "tracing=false" but with enablePluginsTracingByDefault feature flag`,
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "false"}},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: defaultOTelCfg,
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(featuremgmt.FlagEnablePluginsTracingByDefault),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expDefaultOtlp,
|
||||
},
|
||||
{
|
||||
name: "GF_PLUGIN_VERSION is not present if tracing is disabled",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{},
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expGfPluginVersionNotPresent,
|
||||
},
|
||||
{
|
||||
name: "GF_PLUGIN_VERSION is present if tracing is enabled and plugin has version",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: defaultOTelCfg,
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: expGfPluginVersionPresent,
|
||||
},
|
||||
{
|
||||
name: "GF_PLUGIN_VERSION is not present if tracing is enabled but plugin doesn't have a version",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{},
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: pluginWithoutVersion,
|
||||
exp: expGfPluginVersionNotPresent,
|
||||
},
|
||||
{
|
||||
name: "no sampling (neversample)",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{
|
||||
Address: "127.0.0.1:4317",
|
||||
Propagation: "jaeger",
|
||||
Sampler: "",
|
||||
SamplerParam: 0.0,
|
||||
SamplerRemoteURL: "",
|
||||
},
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: func(t *testing.T, envVars []string) {
|
||||
require.Empty(t, getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_TYPE"))
|
||||
require.Equal(t, "0.000000", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_PARAM"))
|
||||
require.Empty(t, getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_REMOTE_URL"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty sampler with param",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{
|
||||
Address: "127.0.0.1:4317",
|
||||
Propagation: "jaeger",
|
||||
Sampler: "",
|
||||
SamplerParam: 0.5,
|
||||
SamplerRemoteURL: "",
|
||||
},
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: func(t *testing.T, envVars []string) {
|
||||
require.Equal(t, "", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_TYPE"))
|
||||
require.Equal(t, "0.500000", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_PARAM"))
|
||||
require.Equal(t, "", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_REMOTE_URL"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "const sampler with param",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{
|
||||
Address: "127.0.0.1:4317",
|
||||
Propagation: "jaeger",
|
||||
Sampler: "const",
|
||||
SamplerParam: 0.5,
|
||||
SamplerRemoteURL: "",
|
||||
},
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: func(t *testing.T, envVars []string) {
|
||||
require.Equal(t, "const", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_TYPE"))
|
||||
require.Equal(t, "0.500000", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_PARAM"))
|
||||
require.Equal(t, "", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_REMOTE_URL"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rateLimiting sampler",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}},
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{
|
||||
Address: "127.0.0.1:4317",
|
||||
Propagation: "jaeger",
|
||||
Sampler: "rateLimiting",
|
||||
SamplerParam: 0.5,
|
||||
SamplerRemoteURL: "",
|
||||
},
|
||||
},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: func(t *testing.T, envVars []string) {
|
||||
require.Equal(t, "rateLimiting", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_TYPE"))
|
||||
require.Equal(t, "0.500000", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_PARAM"))
|
||||
require.Equal(t, "", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_REMOTE_URL"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remote sampler",
|
||||
cfg: &PluginInstanceCfg{
|
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
Tracing: config.Tracing{
|
||||
OpenTelemetry: config.OpenTelemetryCfg{
|
||||
Address: "127.0.0.1:4317",
|
||||
Propagation: "jaeger",
|
||||
Sampler: "remote",
|
||||
SamplerParam: 0.5,
|
||||
SamplerRemoteURL: "127.0.0.1:10001",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugin: defaultPlugin,
|
||||
exp: func(t *testing.T, envVars []string) {
|
||||
require.Equal(t, "remote", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_TYPE"))
|
||||
require.Equal(t, "0.500000", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_PARAM"))
|
||||
require.Equal(t, "127.0.0.1:10001", getEnvVar(envVars, "GF_INSTANCE_OTLP_SAMPLER_REMOTE_URL"))
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := NewEnvVarsProvider(tc.cfg, nil)
|
||||
envVars := p.PluginEnvVars(context.Background(), tc.plugin)
|
||||
tc.exp(t, envVars)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getEnvVarWithExists takes a slice of strings in this format: "K=V" (env vars), and returns the "V" where K = wanted.
|
||||
// If there's no such key, it returns false as the second argument.
|
||||
func getEnvVarWithExists(vars []string, wanted string) (string, bool) {
|
||||
for _, v := range vars {
|
||||
parts := strings.SplitN(v, "=", 2)
|
||||
if parts[0] != wanted {
|
||||
continue
|
||||
}
|
||||
var r string
|
||||
if len(parts) < 2 {
|
||||
r = ""
|
||||
} else {
|
||||
r = parts[1]
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// getEnvVar is like getEnvVarWithExists, but it returns just one string, without the boolean "ok" value.
|
||||
// If the wanted environment variable does not exist, it returns an empty string.
|
||||
func getEnvVar(vars []string, wanted string) string {
|
||||
v, _ := getEnvVarWithExists(vars, wanted)
|
||||
return v
|
||||
}
|
||||
|
||||
func TestPluginEnvVarsProvider_authEnvVars(t *testing.T) {
|
||||
t.Run("backend datasource with auth registration", func(t *testing.T) {
|
||||
p := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test",
|
||||
IAM: &plugindef.IAM{},
|
||||
},
|
||||
ExternalService: &auth.ExternalService{
|
||||
ClientID: "clientID",
|
||||
ClientSecret: "clientSecret",
|
||||
PrivateKey: "privatePem",
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &setting.Cfg{
|
||||
Raw: ini.Empty(),
|
||||
AppURL: "https://myorg.com/",
|
||||
}
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := NewEnvVarsProvider(pCfg, nil)
|
||||
envVars := provider.PluginEnvVars(context.Background(), p)
|
||||
assert.Equal(t, "GF_VERSION=", envVars[0])
|
||||
assert.Equal(t, "GF_APP_URL=https://myorg.com/", envVars[1])
|
||||
assert.Equal(t, "GF_PLUGIN_APP_CLIENT_ID=clientID", envVars[2])
|
||||
assert.Equal(t, "GF_PLUGIN_APP_CLIENT_SECRET=clientSecret", envVars[3])
|
||||
assert.Equal(t, "GF_PLUGIN_APP_PRIVATE_KEY=privatePem", envVars[4])
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginEnvVarsProvider_awsEnvVars(t *testing.T) {
|
||||
t.Run("backend datasource with aws settings", func(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
pluginID string
|
||||
forwardToPlugins []string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "Will generate AWS env vars for plugin as long as is in the forwardToPlugins list",
|
||||
forwardToPlugins: []string{"foobar-datasource", "cloudwatch", "prometheus"},
|
||||
pluginID: "cloudwatch",
|
||||
expected: []string{"GF_VERSION=", "AWS_AUTH_AssumeRoleEnabled=false", "AWS_AUTH_AllowedAuthProviders=grafana_assume_role,keys", "AWS_AUTH_EXTERNAL_ID=mock_external_id", "AWS_AUTH_SESSION_DURATION=10m", "AWS_CW_LIST_METRICS_PAGE_LIMIT=100"},
|
||||
},
|
||||
{
|
||||
name: "Will not generate AWS env vars for plugin as long as is in not the forwardToPlugins list",
|
||||
forwardToPlugins: []string{"cloudwatch", "foobar-datasource"},
|
||||
pluginID: "prometheus",
|
||||
expected: []string{"GF_VERSION="},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
p := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: tc.pluginID,
|
||||
},
|
||||
}
|
||||
cfg := &PluginInstanceCfg{
|
||||
AWSAssumeRoleEnabled: false,
|
||||
AWSAllowedAuthProviders: []string{"grafana_assume_role", "keys"},
|
||||
AWSExternalId: "mock_external_id",
|
||||
AWSSessionDuration: "10m",
|
||||
AWSListMetricsPageLimit: "100",
|
||||
AWSForwardSettingsPlugins: tc.forwardToPlugins,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
provider := NewEnvVarsProvider(cfg, nil)
|
||||
envVars := provider.PluginEnvVars(context.Background(), p)
|
||||
assert.ElementsMatch(t, tc.expected, envVars)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginEnvVarsProvider_featureToggleEnvVar(t *testing.T) {
|
||||
t.Run("backend datasource with feature toggle", func(t *testing.T) {
|
||||
expectedFeatures := []string{"feat-1", "feat-2"}
|
||||
featuresLookup := map[string]bool{
|
||||
expectedFeatures[0]: true,
|
||||
expectedFeatures[1]: true,
|
||||
}
|
||||
|
||||
cfg := &PluginInstanceCfg{
|
||||
Features: featuremgmt.WithFeatures(expectedFeatures[0], true, expectedFeatures[1], true),
|
||||
}
|
||||
|
||||
p := NewEnvVarsProvider(cfg, nil)
|
||||
envVars := p.PluginEnvVars(context.Background(), &plugins.Plugin{})
|
||||
assert.Equal(t, 2, len(envVars))
|
||||
|
||||
toggleExpression := strings.Split(envVars[1], "=")
|
||||
assert.Equal(t, 2, len(toggleExpression))
|
||||
|
||||
assert.Equal(t, "GF_INSTANCE_FEATURE_TOGGLES_ENABLE", toggleExpression[0])
|
||||
|
||||
toggleArgs := toggleExpression[1]
|
||||
features := strings.Split(toggleArgs, ",")
|
||||
|
||||
assert.Equal(t, len(expectedFeatures), len(features))
|
||||
|
||||
// this is necessary because the features are not returned in the order they are provided
|
||||
for _, f := range features {
|
||||
_, ok := featuresLookup[f]
|
||||
assert.True(t, ok)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPluginEnvVarsProvider_azureEnvVars(t *testing.T) {
|
||||
t.Run("backend datasource with azure settings", func(t *testing.T) {
|
||||
cfg := &setting.Cfg{
|
||||
Raw: ini.Empty(),
|
||||
AWSAssumeRoleEnabled: true,
|
||||
Azure: &azsettings.AzureSettings{
|
||||
Cloud: azsettings.AzurePublic,
|
||||
ManagedIdentityEnabled: true,
|
||||
ManagedIdentityClientId: "mock_managed_identity_client_id",
|
||||
WorkloadIdentityEnabled: true,
|
||||
WorkloadIdentitySettings: &azsettings.WorkloadIdentitySettings{
|
||||
TenantId: "mock_workload_identity_tenant_id",
|
||||
ClientId: "mock_workload_identity_client_id",
|
||||
TokenFile: "mock_workload_identity_token_file",
|
||||
},
|
||||
UserIdentityEnabled: true,
|
||||
UserIdentityTokenEndpoint: &azsettings.TokenEndpointSettings{
|
||||
TokenUrl: "mock_user_identity_token_url",
|
||||
ClientId: "mock_user_identity_client_id",
|
||||
ClientSecret: "mock_user_identity_client_secret",
|
||||
UsernameAssertion: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := NewEnvVarsProvider(pCfg, nil)
|
||||
envVars := provider.PluginEnvVars(context.Background(), &plugins.Plugin{})
|
||||
assert.ElementsMatch(t, []string{"GF_VERSION=", "GFAZPL_AZURE_CLOUD=AzureCloud", "GFAZPL_MANAGED_IDENTITY_ENABLED=true",
|
||||
"GFAZPL_MANAGED_IDENTITY_CLIENT_ID=mock_managed_identity_client_id",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_ENABLED=true",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_TENANT_ID=mock_workload_identity_tenant_id",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_CLIENT_ID=mock_workload_identity_client_id",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_TOKEN_FILE=mock_workload_identity_token_file",
|
||||
"GFAZPL_USER_IDENTITY_ENABLED=true",
|
||||
"GFAZPL_USER_IDENTITY_TOKEN_URL=mock_user_identity_token_url",
|
||||
"GFAZPL_USER_IDENTITY_CLIENT_ID=mock_user_identity_client_id",
|
||||
"GFAZPL_USER_IDENTITY_CLIENT_SECRET=mock_user_identity_client_secret",
|
||||
"GFAZPL_USER_IDENTITY_ASSERTION=username",
|
||||
}, envVars)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package pluginconfig
|
||||
|
||||
import "context"
|
||||
|
||||
var _ PluginRequestConfigProvider = (*FakePluginRequestConfigProvider)(nil)
|
||||
|
||||
type FakePluginRequestConfigProvider struct{}
|
||||
|
||||
func NewFakePluginRequestConfigProvider() *FakePluginRequestConfigProvider {
|
||||
return &FakePluginRequestConfigProvider{}
|
||||
}
|
||||
|
||||
// PluginRequestConfig returns a map of configuration that should be passed in a plugin request.
|
||||
func (s *FakePluginRequestConfigProvider) PluginRequestConfig(ctx context.Context, pluginID string) map[string]string {
|
||||
return map[string]string{}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package pluginconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
|
||||
"github.com/grafana/grafana-azure-sdk-go/azsettings"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend/proxy"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/experimental/featuretoggles"
|
||||
)
|
||||
|
||||
var _ PluginRequestConfigProvider = (*RequestConfigProvider)(nil)
|
||||
|
||||
type PluginRequestConfigProvider interface {
|
||||
PluginRequestConfig(ctx context.Context, pluginID string) map[string]string
|
||||
}
|
||||
|
||||
type RequestConfigProvider struct {
|
||||
cfg *PluginInstanceCfg
|
||||
}
|
||||
|
||||
func NewRequestConfigProvider(cfg *PluginInstanceCfg) *RequestConfigProvider {
|
||||
return &RequestConfigProvider{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
// PluginRequestConfig returns a map of configuration that should be passed in a plugin request.
|
||||
// nolint:gocyclo
|
||||
func (s *RequestConfigProvider) PluginRequestConfig(ctx context.Context, pluginID string) map[string]string {
|
||||
m := make(map[string]string)
|
||||
|
||||
if s.cfg.GrafanaAppURL != "" {
|
||||
m[backend.AppURL] = s.cfg.GrafanaAppURL
|
||||
}
|
||||
if s.cfg.ConcurrentQueryCount != 0 {
|
||||
m[backend.ConcurrentQueryCount] = strconv.Itoa(s.cfg.ConcurrentQueryCount)
|
||||
}
|
||||
|
||||
enabledFeatures := s.cfg.Features.GetEnabled(ctx)
|
||||
if len(enabledFeatures) > 0 {
|
||||
features := make([]string, 0, len(enabledFeatures))
|
||||
for feat := range enabledFeatures {
|
||||
features = append(features, feat)
|
||||
}
|
||||
sort.Strings(features)
|
||||
m[featuretoggles.EnabledFeatures] = strings.Join(features, ",")
|
||||
}
|
||||
|
||||
if slices.Contains[[]string, string](s.cfg.AWSForwardSettingsPlugins, pluginID) {
|
||||
if !s.cfg.AWSAssumeRoleEnabled {
|
||||
m[awsds.AssumeRoleEnabledEnvVarKeyName] = "false"
|
||||
}
|
||||
if len(s.cfg.AWSAllowedAuthProviders) > 0 {
|
||||
m[awsds.AllowedAuthProvidersEnvVarKeyName] = strings.Join(s.cfg.AWSAllowedAuthProviders, ",")
|
||||
}
|
||||
if s.cfg.AWSExternalId != "" {
|
||||
m[awsds.GrafanaAssumeRoleExternalIdKeyName] = s.cfg.AWSExternalId
|
||||
}
|
||||
if s.cfg.AWSSessionDuration != "" {
|
||||
m[awsds.SessionDurationEnvVarKeyName] = s.cfg.AWSSessionDuration
|
||||
}
|
||||
if s.cfg.AWSListMetricsPageLimit != "" {
|
||||
m[awsds.ListMetricsPageLimitKeyName] = s.cfg.AWSListMetricsPageLimit
|
||||
}
|
||||
}
|
||||
|
||||
if s.cfg.ProxySettings.Enabled {
|
||||
m[proxy.PluginSecureSocksProxyEnabled] = "true"
|
||||
m[proxy.PluginSecureSocksProxyClientCert] = s.cfg.ProxySettings.ClientCert
|
||||
m[proxy.PluginSecureSocksProxyClientKey] = s.cfg.ProxySettings.ClientKey
|
||||
m[proxy.PluginSecureSocksProxyRootCACert] = s.cfg.ProxySettings.RootCA
|
||||
m[proxy.PluginSecureSocksProxyProxyAddress] = s.cfg.ProxySettings.ProxyAddress
|
||||
m[proxy.PluginSecureSocksProxyServerName] = s.cfg.ProxySettings.ServerName
|
||||
m[proxy.PluginSecureSocksProxyAllowInsecure] = strconv.FormatBool(s.cfg.ProxySettings.AllowInsecure)
|
||||
}
|
||||
|
||||
// Settings here will be extracted by grafana-azure-sdk-go from the plugin context
|
||||
if s.cfg.AzureAuthEnabled {
|
||||
m[azsettings.AzureAuthEnabled] = strconv.FormatBool(s.cfg.AzureAuthEnabled)
|
||||
}
|
||||
azureSettings := s.cfg.Azure
|
||||
if azureSettings != nil && slices.Contains[[]string, string](azureSettings.ForwardSettingsPlugins, pluginID) {
|
||||
if azureSettings.Cloud != "" {
|
||||
m[azsettings.AzureCloud] = azureSettings.Cloud
|
||||
}
|
||||
|
||||
if azureSettings.ManagedIdentityEnabled {
|
||||
m[azsettings.ManagedIdentityEnabled] = "true"
|
||||
|
||||
if azureSettings.ManagedIdentityClientId != "" {
|
||||
m[azsettings.ManagedIdentityClientID] = azureSettings.ManagedIdentityClientId
|
||||
}
|
||||
}
|
||||
|
||||
if azureSettings.UserIdentityEnabled {
|
||||
m[azsettings.UserIdentityEnabled] = "true"
|
||||
|
||||
if azureSettings.UserIdentityTokenEndpoint != nil {
|
||||
if azureSettings.UserIdentityTokenEndpoint.TokenUrl != "" {
|
||||
m[azsettings.UserIdentityTokenURL] = azureSettings.UserIdentityTokenEndpoint.TokenUrl
|
||||
}
|
||||
if azureSettings.UserIdentityTokenEndpoint.ClientId != "" {
|
||||
m[azsettings.UserIdentityClientID] = azureSettings.UserIdentityTokenEndpoint.ClientId
|
||||
}
|
||||
if azureSettings.UserIdentityTokenEndpoint.ClientSecret != "" {
|
||||
m[azsettings.UserIdentityClientSecret] = azureSettings.UserIdentityTokenEndpoint.ClientSecret
|
||||
}
|
||||
if azureSettings.UserIdentityTokenEndpoint.UsernameAssertion {
|
||||
m[azsettings.UserIdentityAssertion] = "username"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if azureSettings.WorkloadIdentityEnabled {
|
||||
m[azsettings.WorkloadIdentityEnabled] = "true"
|
||||
|
||||
if azureSettings.WorkloadIdentitySettings != nil {
|
||||
if azureSettings.WorkloadIdentitySettings.ClientId != "" {
|
||||
m[azsettings.WorkloadIdentityClientID] = azureSettings.WorkloadIdentitySettings.ClientId
|
||||
}
|
||||
if azureSettings.WorkloadIdentitySettings.TenantId != "" {
|
||||
m[azsettings.WorkloadIdentityTenantID] = azureSettings.WorkloadIdentitySettings.TenantId
|
||||
}
|
||||
if azureSettings.WorkloadIdentitySettings.TokenFile != "" {
|
||||
m[azsettings.WorkloadIdentityTokenFile] = azureSettings.WorkloadIdentitySettings.TokenFile
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.cfg.UserFacingDefaultError != "" {
|
||||
m[backend.UserFacingDefaultError] = s.cfg.UserFacingDefaultError
|
||||
}
|
||||
|
||||
if s.cfg.DataProxyRowLimit != 0 {
|
||||
m[backend.SQLRowLimit] = strconv.FormatInt(s.cfg.DataProxyRowLimit, 10)
|
||||
}
|
||||
|
||||
m[backend.SQLMaxOpenConnsDefault] = strconv.Itoa(s.cfg.SQLDatasourceMaxOpenConnsDefault)
|
||||
m[backend.SQLMaxIdleConnsDefault] = strconv.Itoa(s.cfg.SQLDatasourceMaxIdleConnsDefault)
|
||||
m[backend.SQLMaxConnLifetimeSecondsDefault] = strconv.Itoa(s.cfg.SQLDatasourceMaxConnLifetimeDefault)
|
||||
|
||||
return m
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
package pluginconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana-azure-sdk-go/azsettings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func TestRequestConfigProvider_PluginRequestConfig_Defaults(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.Equal(t, map[string]string{
|
||||
"GF_SQL_MAX_OPEN_CONNS_DEFAULT": "0",
|
||||
"GF_SQL_MAX_IDLE_CONNS_DEFAULT": "0",
|
||||
"GF_SQL_MAX_CONN_LIFETIME_SECONDS_DEFAULT": "0",
|
||||
}, p.PluginRequestConfig(context.Background(), ""))
|
||||
}
|
||||
|
||||
func TestRequestConfigProvider_PluginRequestConfig(t *testing.T) {
|
||||
tcs := []struct {
|
||||
name string
|
||||
cfg *PluginInstanceCfg
|
||||
expected map[string]string
|
||||
}{
|
||||
{
|
||||
name: "Both features and proxy settings enabled",
|
||||
cfg: &PluginInstanceCfg{
|
||||
ProxySettings: setting.SecureSocksDSProxySettings{
|
||||
Enabled: true,
|
||||
ShowUI: true,
|
||||
ClientCert: "c3rt",
|
||||
ClientKey: "k3y",
|
||||
RootCA: "ca",
|
||||
ProxyAddress: "https://proxy.grafana.com",
|
||||
ServerName: "secureProxy",
|
||||
AllowInsecure: true,
|
||||
},
|
||||
Features: featuremgmt.WithFeatures("feat-2", "feat-500", "feat-1"),
|
||||
},
|
||||
expected: map[string]string{
|
||||
"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "feat-1,feat-2,feat-500",
|
||||
"GF_SECURE_SOCKS_DATASOURCE_PROXY_SERVER_ENABLED": "true",
|
||||
"GF_SECURE_SOCKS_DATASOURCE_PROXY_CLIENT_CERT": "c3rt",
|
||||
"GF_SECURE_SOCKS_DATASOURCE_PROXY_CLIENT_KEY": "k3y",
|
||||
"GF_SECURE_SOCKS_DATASOURCE_PROXY_ROOT_CA_CERT": "ca",
|
||||
"GF_SECURE_SOCKS_DATASOURCE_PROXY_PROXY_ADDRESS": "https://proxy.grafana.com",
|
||||
"GF_SECURE_SOCKS_DATASOURCE_PROXY_SERVER_NAME": "secureProxy",
|
||||
"GF_SECURE_SOCKS_DATASOURCE_PROXY_ALLOW_INSECURE": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Features enabled but proxy settings disabled",
|
||||
cfg: &PluginInstanceCfg{
|
||||
ProxySettings: setting.SecureSocksDSProxySettings{
|
||||
Enabled: false,
|
||||
ShowUI: true,
|
||||
ClientCert: "c3rt",
|
||||
ClientKey: "k3y",
|
||||
RootCA: "ca",
|
||||
ProxyAddress: "https://proxy.grafana.com",
|
||||
ServerName: "secureProxy",
|
||||
},
|
||||
Features: featuremgmt.WithFeatures("feat-2", "feat-500", "feat-1"),
|
||||
},
|
||||
expected: map[string]string{
|
||||
"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "feat-1,feat-2,feat-500",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Both features and proxy settings disabled",
|
||||
cfg: &PluginInstanceCfg{
|
||||
ProxySettings: setting.SecureSocksDSProxySettings{
|
||||
Enabled: false,
|
||||
ShowUI: true,
|
||||
ClientCert: "c3rt",
|
||||
ClientKey: "k3y",
|
||||
RootCA: "ca",
|
||||
ProxyAddress: "https://proxy.grafana.com",
|
||||
ServerName: "secureProxy",
|
||||
},
|
||||
Features: featuremgmt.WithFeatures("feat-2", false),
|
||||
},
|
||||
expected: map[string]string{},
|
||||
},
|
||||
{
|
||||
name: "Both features and proxy settings empty",
|
||||
cfg: &PluginInstanceCfg{
|
||||
ProxySettings: setting.SecureSocksDSProxySettings{},
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
},
|
||||
expected: map[string]string{},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := NewRequestConfigProvider(tc.cfg)
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), ""), tc.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequestConfigProvider_PluginRequestConfig_featureToggles(t *testing.T) {
|
||||
t.Run("Feature toggles list is deterministic", func(t *testing.T) {
|
||||
tcs := []struct {
|
||||
features featuremgmt.FeatureToggles
|
||||
expectedConfig map[string]string
|
||||
}{
|
||||
{
|
||||
features: featuremgmt.WithFeatures(),
|
||||
expectedConfig: map[string]string{},
|
||||
},
|
||||
{
|
||||
features: featuremgmt.WithFeatures("A", "B", "C"),
|
||||
expectedConfig: map[string]string{"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "A,B,C"},
|
||||
},
|
||||
{
|
||||
features: featuremgmt.WithFeatures("C", "B", "A"),
|
||||
expectedConfig: map[string]string{"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "A,B,C"},
|
||||
},
|
||||
{
|
||||
features: featuremgmt.WithFeatures("b", "a", "c", "d"),
|
||||
expectedConfig: map[string]string{"GF_INSTANCE_FEATURE_TOGGLES_ENABLE": "a,b,c,d"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
cfg := setting.NewCfg()
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), tc.features)
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), ""), tc.expectedConfig)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestConfigProvider_PluginRequestConfig_appURL(t *testing.T) {
|
||||
t.Run("Uses the configured app URL", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.AppURL = "https://myorg.com/"
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), ""), map[string]string{"GF_APP_URL": "https://myorg.com/"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestConfigProvider_PluginRequestConfig_SQL(t *testing.T) {
|
||||
t.Run("Uses the configured values", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.DataProxyRowLimit = 23
|
||||
cfg.SqlDatasourceMaxOpenConnsDefault = 24
|
||||
cfg.SqlDatasourceMaxIdleConnsDefault = 25
|
||||
cfg.SqlDatasourceMaxConnLifetimeDefault = 26
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), ""), map[string]string{
|
||||
"GF_SQL_ROW_LIMIT": "23",
|
||||
"GF_SQL_MAX_OPEN_CONNS_DEFAULT": "24",
|
||||
"GF_SQL_MAX_IDLE_CONNS_DEFAULT": "25",
|
||||
"GF_SQL_MAX_CONN_LIFETIME_SECONDS_DEFAULT": "26",
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Uses the configured max-default-values, even when they are zero", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.SqlDatasourceMaxOpenConnsDefault = 0
|
||||
cfg.SqlDatasourceMaxIdleConnsDefault = 0
|
||||
cfg.SqlDatasourceMaxConnLifetimeDefault = 0
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.Equal(t, map[string]string{
|
||||
"GF_SQL_MAX_OPEN_CONNS_DEFAULT": "0",
|
||||
"GF_SQL_MAX_IDLE_CONNS_DEFAULT": "0",
|
||||
"GF_SQL_MAX_CONN_LIFETIME_SECONDS_DEFAULT": "0",
|
||||
}, p.PluginRequestConfig(context.Background(), ""))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestConfigProvider_PluginRequestConfig_concurrentQueryCount(t *testing.T) {
|
||||
t.Run("Uses the configured concurrent query count", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.ConcurrentQueryCount = 42
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), ""), map[string]string{"GF_CONCURRENT_QUERY_COUNT": "42"})
|
||||
})
|
||||
|
||||
t.Run("Doesn't set the concurrent query count if it is not in the config", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.NotContains(t, p.PluginRequestConfig(context.Background(), ""), "GF_CONCURRENT_QUERY_COUNT")
|
||||
})
|
||||
|
||||
t.Run("Doesn't set the concurrent query count if it is zero", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.ConcurrentQueryCount = 0
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.NotContains(t, p.PluginRequestConfig(context.Background(), ""), "GF_CONCURRENT_QUERY_COUNT")
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestConfigProvider_PluginRequestConfig_azureAuthEnabled(t *testing.T) {
|
||||
t.Run("Uses the configured azureAuthEnabled", func(t *testing.T) {
|
||||
cfg := &PluginInstanceCfg{
|
||||
AzureAuthEnabled: true,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
p := NewRequestConfigProvider(cfg)
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), ""), map[string]string{"GFAZPL_AZURE_AUTH_ENABLED": "true"})
|
||||
})
|
||||
|
||||
t.Run("Doesn't set the azureAuthEnabled if it is not in the config", func(t *testing.T) {
|
||||
cfg := &PluginInstanceCfg{
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
p := NewRequestConfigProvider(cfg)
|
||||
require.NotContains(t, p.PluginRequestConfig(context.Background(), ""), "GFAZPL_AZURE_AUTH_ENABLED")
|
||||
})
|
||||
|
||||
t.Run("Doesn't set the azureAuthEnabled if it is false", func(t *testing.T) {
|
||||
cfg := &PluginInstanceCfg{
|
||||
AzureAuthEnabled: false,
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
p := NewRequestConfigProvider(cfg)
|
||||
require.NotContains(t, p.PluginRequestConfig(context.Background(), ""), "GFAZPL_AZURE_AUTH_ENABLED")
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestConfigProvider_PluginRequestConfig_azure(t *testing.T) {
|
||||
azSettings := &azsettings.AzureSettings{
|
||||
Cloud: azsettings.AzurePublic,
|
||||
ManagedIdentityEnabled: true,
|
||||
ManagedIdentityClientId: "mock_managed_identity_client_id",
|
||||
WorkloadIdentityEnabled: true,
|
||||
WorkloadIdentitySettings: &azsettings.WorkloadIdentitySettings{
|
||||
TenantId: "mock_workload_identity_tenant_id",
|
||||
ClientId: "mock_workload_identity_client_id",
|
||||
TokenFile: "mock_workload_identity_token_file",
|
||||
},
|
||||
UserIdentityEnabled: true,
|
||||
UserIdentityTokenEndpoint: &azsettings.TokenEndpointSettings{
|
||||
TokenUrl: "mock_user_identity_token_url",
|
||||
ClientId: "mock_user_identity_client_id",
|
||||
ClientSecret: "mock_user_identity_client_secret",
|
||||
UsernameAssertion: true,
|
||||
},
|
||||
ForwardSettingsPlugins: []string{"grafana-azure-monitor-datasource", "prometheus", "grafana-azure-data-explorer-datasource", "mssql"},
|
||||
}
|
||||
|
||||
t.Run("uses the azure settings for an Azure plugin", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Azure = azSettings
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), "grafana-azure-monitor-datasource"), map[string]string{
|
||||
"GFAZPL_AZURE_CLOUD": "AzureCloud", "GFAZPL_MANAGED_IDENTITY_ENABLED": "true",
|
||||
"GFAZPL_MANAGED_IDENTITY_CLIENT_ID": "mock_managed_identity_client_id",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_ENABLED": "true",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_TENANT_ID": "mock_workload_identity_tenant_id",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_CLIENT_ID": "mock_workload_identity_client_id",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_TOKEN_FILE": "mock_workload_identity_token_file",
|
||||
"GFAZPL_USER_IDENTITY_ENABLED": "true",
|
||||
"GFAZPL_USER_IDENTITY_TOKEN_URL": "mock_user_identity_token_url",
|
||||
"GFAZPL_USER_IDENTITY_CLIENT_ID": "mock_user_identity_client_id",
|
||||
"GFAZPL_USER_IDENTITY_CLIENT_SECRET": "mock_user_identity_client_secret",
|
||||
"GFAZPL_USER_IDENTITY_ASSERTION": "username",
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("does not use the azure settings for a non-Azure plugin", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Azure = azSettings
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
m := p.PluginRequestConfig(context.Background(), "")
|
||||
require.NotContains(t, m, "GFAZPL_AZURE_CLOUD")
|
||||
require.NotContains(t, m, "GFAZPL_MANAGED_IDENTITY_ENABLED")
|
||||
require.NotContains(t, m, "GFAZPL_MANAGED_IDENTITY_CLIENT_ID")
|
||||
require.NotContains(t, m, "GFAZPL_WORKLOAD_IDENTITY_ENABLED")
|
||||
require.NotContains(t, m, "GFAZPL_WORKLOAD_IDENTITY_TENANT_ID")
|
||||
require.NotContains(t, m, "GFAZPL_WORKLOAD_IDENTITY_CLIENT_ID")
|
||||
require.NotContains(t, m, "GFAZPL_WORKLOAD_IDENTITY_TOKEN_FILE")
|
||||
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_ENABLED")
|
||||
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_TOKEN_URL")
|
||||
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_CLIENT_ID")
|
||||
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_CLIENT_SECRET")
|
||||
require.NotContains(t, m, "GFAZPL_USER_IDENTITY_ASSERTION")
|
||||
})
|
||||
|
||||
t.Run("uses the azure settings for a non-Azure user-specified plugin", func(t *testing.T) {
|
||||
azSettings.ForwardSettingsPlugins = append(azSettings.ForwardSettingsPlugins, "test-datasource")
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Azure = azSettings
|
||||
|
||||
pCfg, err := ProvidePluginInstanceConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
p := NewRequestConfigProvider(pCfg)
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), "test-datasource"), map[string]string{
|
||||
"GFAZPL_AZURE_CLOUD": "AzureCloud", "GFAZPL_MANAGED_IDENTITY_ENABLED": "true",
|
||||
"GFAZPL_MANAGED_IDENTITY_CLIENT_ID": "mock_managed_identity_client_id",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_ENABLED": "true",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_TENANT_ID": "mock_workload_identity_tenant_id",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_CLIENT_ID": "mock_workload_identity_client_id",
|
||||
"GFAZPL_WORKLOAD_IDENTITY_TOKEN_FILE": "mock_workload_identity_token_file",
|
||||
"GFAZPL_USER_IDENTITY_ENABLED": "true",
|
||||
"GFAZPL_USER_IDENTITY_TOKEN_URL": "mock_user_identity_token_url",
|
||||
"GFAZPL_USER_IDENTITY_CLIENT_ID": "mock_user_identity_client_id",
|
||||
"GFAZPL_USER_IDENTITY_CLIENT_SECRET": "mock_user_identity_client_secret",
|
||||
"GFAZPL_USER_IDENTITY_ASSERTION": "username",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequestConfigProvider_PluginRequestConfig_aws(t *testing.T) {
|
||||
cfg := &PluginInstanceCfg{
|
||||
Features: featuremgmt.WithFeatures(),
|
||||
}
|
||||
|
||||
cfg.AWSAssumeRoleEnabled = false
|
||||
cfg.AWSAllowedAuthProviders = []string{"grafana_assume_role", "keys"}
|
||||
cfg.AWSExternalId = "mock_external_id"
|
||||
cfg.AWSSessionDuration = "10m"
|
||||
cfg.AWSListMetricsPageLimit = "100"
|
||||
cfg.AWSForwardSettingsPlugins = []string{"cloudwatch", "prometheus", "elasticsearch"}
|
||||
|
||||
p := NewRequestConfigProvider(cfg)
|
||||
|
||||
t.Run("uses the aws settings for an AWS plugin", func(t *testing.T) {
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), "cloudwatch"), map[string]string{
|
||||
"AWS_AUTH_AssumeRoleEnabled": "false",
|
||||
"AWS_AUTH_AllowedAuthProviders": "grafana_assume_role,keys",
|
||||
"AWS_AUTH_EXTERNAL_ID": "mock_external_id",
|
||||
"AWS_AUTH_SESSION_DURATION": "10m",
|
||||
"AWS_CW_LIST_METRICS_PAGE_LIMIT": "100",
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("does not use the aws settings for a non-aws plugin", func(t *testing.T) {
|
||||
m := p.PluginRequestConfig(context.Background(), "")
|
||||
require.NotContains(t, m, "AWS_AUTH_AssumeRoleEnabled")
|
||||
require.NotContains(t, m, "AWS_AUTH_AllowedAuthProviders")
|
||||
require.NotContains(t, m, "AWS_AUTH_EXTERNAL_ID")
|
||||
require.NotContains(t, m, "AWS_AUTH_SESSION_DURATION")
|
||||
require.NotContains(t, m, "AWS_CW_LIST_METRICS_PAGE_LIMIT")
|
||||
})
|
||||
|
||||
t.Run("uses the aws settings for a non-aws user-specified plugin", func(t *testing.T) {
|
||||
cfg.AWSForwardSettingsPlugins = append(cfg.AWSForwardSettingsPlugins, "test-datasource")
|
||||
|
||||
p = NewRequestConfigProvider(cfg)
|
||||
require.Subset(t, p.PluginRequestConfig(context.Background(), "test-datasource"), map[string]string{
|
||||
"AWS_AUTH_AssumeRoleEnabled": "false",
|
||||
"AWS_AUTH_AllowedAuthProviders": "grafana_assume_role,keys",
|
||||
"AWS_AUTH_EXTERNAL_ID": "mock_external_id",
|
||||
"AWS_AUTH_SESSION_DURATION": "10m",
|
||||
"AWS_CW_LIST_METRICS_PAGE_LIMIT": "100",
|
||||
})
|
||||
})
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package config
|
||||
package pluginconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package config
|
||||
package pluginconfig
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -15,11 +15,10 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/plugins/envvars"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/adapters"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -32,28 +31,28 @@ const (
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, cacheService *localcache.CacheService, pluginStore pluginstore.Store,
|
||||
dataSourceCache datasources.CacheService, dataSourceService datasources.DataSourceService,
|
||||
pluginSettingsService pluginsettings.Service, licensing plugins.Licensing, pCfg *config.Cfg) *Provider {
|
||||
pluginSettingsService pluginsettings.Service, pluginRequestConfigProvider pluginconfig.PluginRequestConfigProvider) *Provider {
|
||||
return &Provider{
|
||||
cfg: cfg,
|
||||
cacheService: cacheService,
|
||||
pluginStore: pluginStore,
|
||||
dataSourceCache: dataSourceCache,
|
||||
dataSourceService: dataSourceService,
|
||||
pluginSettingsService: pluginSettingsService,
|
||||
pluginEnvVars: envvars.NewProvider(pCfg, licensing),
|
||||
logger: log.New("plugin.context"),
|
||||
cfg: cfg,
|
||||
cacheService: cacheService,
|
||||
pluginStore: pluginStore,
|
||||
dataSourceCache: dataSourceCache,
|
||||
dataSourceService: dataSourceService,
|
||||
pluginSettingsService: pluginSettingsService,
|
||||
pluginRequestConfigProvider: pluginRequestConfigProvider,
|
||||
logger: log.New("plugin.context"),
|
||||
}
|
||||
}
|
||||
|
||||
type Provider struct {
|
||||
cfg *setting.Cfg
|
||||
pluginEnvVars *envvars.Service
|
||||
cacheService *localcache.CacheService
|
||||
pluginStore pluginstore.Store
|
||||
dataSourceCache datasources.CacheService
|
||||
dataSourceService datasources.DataSourceService
|
||||
pluginSettingsService pluginsettings.Service
|
||||
logger log.Logger
|
||||
cfg *setting.Cfg
|
||||
pluginRequestConfigProvider pluginconfig.PluginRequestConfigProvider
|
||||
cacheService *localcache.CacheService
|
||||
pluginStore pluginstore.Store
|
||||
dataSourceCache datasources.CacheService
|
||||
dataSourceService datasources.DataSourceService
|
||||
pluginSettingsService pluginsettings.Service
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
// Get will retrieve plugin context by the provided pluginID and orgID.
|
||||
@@ -83,7 +82,7 @@ func (p *Provider) Get(ctx context.Context, pluginID string, user identity.Reque
|
||||
pCtx.AppInstanceSettings = appSettings
|
||||
}
|
||||
|
||||
settings := p.pluginEnvVars.GetConfigMap(ctx, pluginID, plugin.ExternalService)
|
||||
settings := p.pluginRequestConfigProvider.PluginRequestConfig(ctx, pluginID)
|
||||
pCtx.GrafanaConfig = backend.NewGrafanaCfg(settings)
|
||||
|
||||
ua, err := useragent.New(p.cfg.BuildVersion, runtime.GOOS, runtime.GOARCH)
|
||||
@@ -120,7 +119,7 @@ func (p *Provider) GetWithDataSource(ctx context.Context, pluginID string, user
|
||||
}
|
||||
pCtx.DataSourceInstanceSettings = datasourceSettings
|
||||
|
||||
settings := p.pluginEnvVars.GetConfigMap(ctx, pluginID, plugin.ExternalService)
|
||||
settings := p.pluginRequestConfigProvider.PluginRequestConfig(ctx, pluginID)
|
||||
pCtx.GrafanaConfig = backend.NewGrafanaCfg(settings)
|
||||
|
||||
ua, err := useragent.New(p.cfg.BuildVersion, runtime.GOOS, runtime.GOARCH)
|
||||
@@ -168,7 +167,7 @@ func (p *Provider) PluginContextForDataSource(ctx context.Context, datasourceSet
|
||||
|
||||
pCtx.DataSourceInstanceSettings = datasourceSettings
|
||||
|
||||
settings := p.pluginEnvVars.GetConfigMap(ctx, pluginID, plugin.ExternalService)
|
||||
settings := p.pluginRequestConfigProvider.PluginRequestConfig(ctx, pluginID)
|
||||
pCtx.GrafanaConfig = backend.NewGrafanaCfg(settings)
|
||||
|
||||
ua, err := useragent.New(p.cfg.BuildVersion, runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
@@ -10,11 +10,11 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
pluginFakes "github.com/grafana/grafana/pkg/plugins/manager/fakes"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/registry"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
|
||||
@@ -43,7 +43,7 @@ func TestGet(t *testing.T) {
|
||||
db := &dbtest.FakeDB{ExpectedError: pluginsettings.ErrPluginSettingNotFound}
|
||||
pcp := plugincontext.ProvideService(cfg, localcache.ProvideService(),
|
||||
pluginstore.New(preg, &pluginFakes.FakeLoader{}), &fakeDatasources.FakeCacheService{},
|
||||
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()), pluginFakes.NewFakeLicensingService(), &config.Cfg{},
|
||||
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider(),
|
||||
)
|
||||
identity := &user.SignedInUser{OrgID: int64(1), Login: "admin"}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
||||
pCfg "github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/plugins/envvars"
|
||||
"github.com/grafana/grafana/pkg/plugins/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/client"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/filestore"
|
||||
@@ -35,13 +36,13 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/angularinspector"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/angularpatternsstore"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/clientmiddleware"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/config"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/keyretriever"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/keyretriever/dynamic"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/keystore"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/loader"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginexternal"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
@@ -55,7 +56,12 @@ import (
|
||||
|
||||
// WireSet provides a wire.ProviderSet of plugin providers.
|
||||
var WireSet = wire.NewSet(
|
||||
config.ProvideConfig,
|
||||
pluginconfig.ProvidePluginManagementConfig,
|
||||
pluginconfig.ProvidePluginInstanceConfig,
|
||||
pluginconfig.NewEnvVarsProvider,
|
||||
wire.Bind(new(envvars.Provider), new(*pluginconfig.EnvVarsProvider)),
|
||||
pluginconfig.NewRequestConfigProvider,
|
||||
wire.Bind(new(pluginconfig.PluginRequestConfigProvider), new(*pluginconfig.RequestConfigProvider)),
|
||||
pluginstore.ProvideService,
|
||||
wire.Bind(new(pluginstore.Store), new(*pluginstore.Service)),
|
||||
wire.Bind(new(plugins.SecretsPluginManager), new(*pluginstore.Service)),
|
||||
@@ -130,7 +136,7 @@ var WireExtensionSet = wire.NewSet(
|
||||
)
|
||||
|
||||
func ProvideClientDecorator(
|
||||
cfg *setting.Cfg, pCfg *pCfg.Cfg,
|
||||
cfg *setting.Cfg, pCfg *pCfg.PluginManagementCfg,
|
||||
pluginRegistry registry.Service,
|
||||
oAuthTokenService oauthtoken.OAuthTokenService,
|
||||
tracer tracing.Tracer,
|
||||
@@ -142,7 +148,7 @@ func ProvideClientDecorator(
|
||||
}
|
||||
|
||||
func NewClientDecorator(
|
||||
cfg *setting.Cfg, pCfg *pCfg.Cfg,
|
||||
cfg *setting.Cfg, pCfg *pCfg.PluginManagementCfg,
|
||||
pluginRegistry registry.Service, oAuthTokenService oauthtoken.OAuthTokenService,
|
||||
tracer tracing.Tracer, cachingService caching.CachingService, features *featuremgmt.FeatureManager,
|
||||
promRegisterer prometheus.Registerer, registry registry.Service,
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/provider"
|
||||
"github.com/grafana/grafana/pkg/plugins/config"
|
||||
pluginscfg "github.com/grafana/grafana/pkg/plugins/config"
|
||||
"github.com/grafana/grafana/pkg/plugins/envvars"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/loader"
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/pipeline/bootstrap"
|
||||
@@ -21,10 +21,12 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/sources"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline"
|
||||
"github.com/grafana/grafana/pkg/services/rendering"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func ProvideService(cfg *config.Cfg, registry registry.Service, licensing plugins.Licensing) (*Manager, error) {
|
||||
l, err := createLoader(cfg, registry, licensing)
|
||||
func ProvideService(cfg *setting.Cfg, pCfg *pluginscfg.PluginManagementCfg, pluginEnvProvider envvars.Provider, registry registry.Service,
|
||||
licensing plugins.Licensing) (*Manager, error) {
|
||||
l, err := createLoader(cfg, pCfg, pluginEnvProvider, registry, licensing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -33,14 +35,14 @@ func ProvideService(cfg *config.Cfg, registry registry.Service, licensing plugin
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
cfg *config.Cfg
|
||||
cfg *setting.Cfg
|
||||
loader loader.Service
|
||||
log log.Logger
|
||||
|
||||
renderer *Plugin
|
||||
}
|
||||
|
||||
func NewManager(cfg *config.Cfg, loader loader.Service) *Manager {
|
||||
func NewManager(cfg *setting.Cfg, loader loader.Service) *Manager {
|
||||
return &Manager{
|
||||
cfg: cfg,
|
||||
loader: loader,
|
||||
@@ -102,8 +104,9 @@ func (m *Manager) Renderer(ctx context.Context) (rendering.Plugin, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func createLoader(cfg *config.Cfg, pr registry.Service, l plugins.Licensing) (loader.Service, error) {
|
||||
d := discovery.New(cfg, discovery.Opts{
|
||||
func createLoader(cfg *setting.Cfg, pCfg *pluginscfg.PluginManagementCfg, pluginEnvProvider envvars.Provider,
|
||||
pr registry.Service, l plugins.Licensing) (loader.Service, error) {
|
||||
d := discovery.New(pCfg, discovery.Opts{
|
||||
FindFilterFuncs: []discovery.FindFilterFunc{
|
||||
discovery.NewPermittedPluginTypesFilterStep([]plugins.Type{plugins.TypeRenderer}),
|
||||
func(ctx context.Context, class plugins.Class, bundles []*plugins.FoundBundle) ([]*plugins.FoundBundle, error) {
|
||||
@@ -111,21 +114,21 @@ func createLoader(cfg *config.Cfg, pr registry.Service, l plugins.Licensing) (lo
|
||||
},
|
||||
},
|
||||
})
|
||||
b := bootstrap.New(cfg, bootstrap.Opts{
|
||||
b := bootstrap.New(pCfg, bootstrap.Opts{
|
||||
DecorateFuncs: []bootstrap.DecorateFunc{}, // no decoration required
|
||||
})
|
||||
v := validation.New(cfg, validation.Opts{
|
||||
v := validation.New(pCfg, validation.Opts{
|
||||
ValidateFuncs: []validation.ValidateFunc{
|
||||
validation.SignatureValidationStep(signature.NewValidator(signature.NewUnsignedAuthorizer(cfg))),
|
||||
validation.SignatureValidationStep(signature.NewValidator(signature.NewUnsignedAuthorizer(pCfg))),
|
||||
},
|
||||
})
|
||||
i := initialization.New(cfg, initialization.Opts{
|
||||
i := initialization.New(pCfg, initialization.Opts{
|
||||
InitializeFuncs: []initialization.InitializeFunc{
|
||||
initialization.BackendClientInitStep(envvars.NewProvider(cfg, l), provider.New(provider.RendererProvider)),
|
||||
initialization.BackendClientInitStep(pluginEnvProvider, provider.New(provider.RendererProvider)),
|
||||
initialization.PluginRegistrationStep(pr),
|
||||
},
|
||||
})
|
||||
t, err := termination.New(cfg, termination.Opts{
|
||||
t, err := termination.New(pCfg, termination.Opts{
|
||||
TerminateFuncs: []termination.TerminateFunc{
|
||||
termination.DeregisterStep(pr),
|
||||
},
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"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/setting"
|
||||
)
|
||||
|
||||
func TestRenderer(t *testing.T) {
|
||||
@@ -33,7 +33,7 @@ func TestRenderer(t *testing.T) {
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
cfg := &config.Cfg{
|
||||
cfg := &setting.Cfg{
|
||||
PluginsPath: filepath.Join(testdataDir),
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ func TestRenderer(t *testing.T) {
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
cfg := &config.Cfg{
|
||||
cfg := &setting.Cfg{
|
||||
PluginsPath: filepath.Join(testdataDir),
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ type Service struct {
|
||||
settingsSvc pluginsettings.Service
|
||||
}
|
||||
|
||||
func ProvideService(cfg *config.Cfg, reg extsvcauth.ExternalServiceRegistry, settingsSvc pluginsettings.Service) *Service {
|
||||
func ProvideService(cfg *config.PluginManagementCfg, reg extsvcauth.ExternalServiceRegistry, settingsSvc pluginsettings.Service) *Service {
|
||||
s := &Service{
|
||||
featureEnabled: cfg.Features.IsEnabledGlobally(featuremgmt.FlagExternalServiceAccounts),
|
||||
log: log.New("plugins.external.registration"),
|
||||
|
||||
@@ -28,8 +28,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/manager/sources"
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginscdn"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/config"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pipeline"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginerrs"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -42,7 +42,7 @@ type IntegrationTestCtx struct {
|
||||
}
|
||||
|
||||
func CreateIntegrationTestCtx(t *testing.T, cfg *setting.Cfg, coreRegistry *coreplugin.Registry) *IntegrationTestCtx {
|
||||
pCfg, err := config.ProvideConfig(setting.ProvideProvider(cfg), cfg, featuremgmt.WithFeatures())
|
||||
pCfg, err := pluginconfig.ProvidePluginManagementConfig(cfg, setting.ProvideProvider(cfg), featuremgmt.WithFeatures())
|
||||
require.NoError(t, err)
|
||||
|
||||
cdn := pluginscdn.ProvideService(pCfg)
|
||||
@@ -54,7 +54,7 @@ func CreateIntegrationTestCtx(t *testing.T, cfg *setting.Cfg, coreRegistry *core
|
||||
disc := pipeline.ProvideDiscoveryStage(pCfg, finder.NewLocalFinder(true, pCfg.Features), reg)
|
||||
boot := pipeline.ProvideBootstrapStage(pCfg, signature.ProvideService(pCfg, statickey.New()), assetpath.ProvideService(pCfg, cdn))
|
||||
valid := pipeline.ProvideValidationStage(pCfg, signature.NewValidator(signature.NewUnsignedAuthorizer(pCfg)), angularInspector, errTracker)
|
||||
init := pipeline.ProvideInitializationStage(pCfg, reg, fakes.NewFakeLicensingService(), provider.ProvideService(coreRegistry), proc, &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry())
|
||||
init := pipeline.ProvideInitializationStage(pCfg, reg, provider.ProvideService(coreRegistry), proc, &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry(), nil)
|
||||
term, err := pipeline.ProvideTerminationStage(pCfg, reg, proc)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -84,7 +84,7 @@ type LoaderOpts struct {
|
||||
Initializer initialization.Initializer
|
||||
}
|
||||
|
||||
func CreateTestLoader(t *testing.T, cfg *pluginsCfg.Cfg, opts LoaderOpts) *loader.Loader {
|
||||
func CreateTestLoader(t *testing.T, cfg *pluginsCfg.PluginManagementCfg, opts LoaderOpts) *loader.Loader {
|
||||
if opts.Discoverer == nil {
|
||||
opts.Discoverer = pipeline.ProvideDiscoveryStage(cfg, finder.NewLocalFinder(cfg.DevMode, cfg.Features), registry.ProvideService())
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func CreateTestLoader(t *testing.T, cfg *pluginsCfg.Cfg, opts LoaderOpts) *loade
|
||||
if opts.Initializer == nil {
|
||||
reg := registry.ProvideService()
|
||||
coreRegistry := coreplugin.NewRegistry(make(map[string]backendplugin.PluginFactoryFunc))
|
||||
opts.Initializer = pipeline.ProvideInitializationStage(cfg, reg, fakes.NewFakeLicensingService(), provider.ProvideService(coreRegistry), process.ProvideService(), &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry())
|
||||
opts.Initializer = pipeline.ProvideInitializationStage(cfg, reg, provider.ProvideService(coreRegistry), process.ProvideService(), &fakes.FakeAuthService{}, fakes.NewFakeRoleRegistry(), nil)
|
||||
}
|
||||
|
||||
if opts.Terminator == nil {
|
||||
|
||||
Reference in New Issue
Block a user