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:
Will Browne
2024-02-27 12:38:02 +01:00
committed by GitHub
parent d04c579fc2
commit 5edd96ae77
62 changed files with 1933 additions and 1207 deletions
+1 -1
View File
@@ -71,7 +71,7 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg, features featuremgmt.F
grafanaUpdateChecker: &updatechecker.GrafanaService{},
AccessControl: accesscontrolmock.New(),
PluginSettings: pluginsSettings,
pluginsCDNService: pluginscdn.ProvideService(&config.Cfg{
pluginsCDNService: pluginscdn.ProvideService(&config.PluginManagementCfg{
PluginsCDNURLTemplate: cfg.PluginsCDNURLTemplate,
PluginSettings: cfg.PluginSettings,
}),
+5 -5
View File
@@ -20,11 +20,11 @@ import (
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/config"
pluginClient "github.com/grafana/grafana/pkg/plugins/manager/client"
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/featuremgmt"
"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"
@@ -78,7 +78,7 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
},
},
}, &fakeDatasources.FakeCacheService{}, &fakeDatasources.FakeDataSourceService{},
pluginSettings.ProvideService(dbtest.NewFakeDB(), secretstest.NewFakeSecretsService()), pluginFakes.NewFakeLicensingService(), &config.Cfg{}),
pluginSettings.ProvideService(dbtest.NewFakeDB(), secretstest.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider()),
)
serverFeatureEnabled := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.queryDataService = qds
@@ -125,7 +125,7 @@ func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
},
},
&fakeDatasources.FakeCacheService{},
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()), pluginFakes.NewFakeLicensingService(), &config.Cfg{},
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider(),
)
qds := query.ProvideService(
cfg,
@@ -297,13 +297,13 @@ func TestDataSourceQueryError(t *testing.T) {
&fakeDatasources.FakeCacheService{},
nil,
&fakePluginRequestValidator{},
pluginClient.ProvideService(r, &config.Cfg{}),
pluginClient.ProvideService(r, &config.PluginManagementCfg{}),
plugincontext.ProvideService(cfg, localcache.ProvideService(), &pluginstore.FakePluginStore{
PluginList: []pluginstore.Plugin{pluginstore.ToGrafanaDTO(p)},
},
&fakeDatasources.FakeCacheService{}, ds,
pluginSettings.ProvideService(dbtest.NewFakeDB(),
secretstest.NewFakeSecretsService()), pluginFakes.NewFakeLicensingService(), &config.Cfg{}),
secretstest.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider()),
)
hs.QuotaService = quotatest.New(false, nil)
})
+2 -3
View File
@@ -21,7 +21,6 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/plugins/config"
pluginClient "github.com/grafana/grafana/pkg/plugins/manager/client"
"github.com/grafana/grafana/pkg/plugins/manager/fakes"
"github.com/grafana/grafana/pkg/services/accesscontrol"
@@ -31,6 +30,7 @@ import (
"github.com/grafana/grafana/pkg/services/oauthtoken/oauthtokentest"
"github.com/grafana/grafana/pkg/services/pluginsintegration"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginaccesscontrol"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
@@ -49,7 +49,6 @@ func TestCallResource(t *testing.T) {
cfg := setting.NewCfg()
cfg.StaticRootPath = staticRootPath
cfg.Azure = &azsettings.AzureSettings{}
pCfg := config.Cfg{}
coreRegistry := coreplugin.ProvideCoreRegistry(tracing.InitializeTracerForTest(), nil, &cloudwatch.CloudWatchService{}, nil, nil, nil, nil,
nil, nil, nil, nil, testdatasource.ProvideService(), nil, nil, nil, nil, nil, nil)
@@ -57,7 +56,7 @@ func TestCallResource(t *testing.T) {
testCtx := pluginsintegration.CreateIntegrationTestCtx(t, cfg, coreRegistry)
pcp := plugincontext.ProvideService(cfg, localcache.ProvideService(), testCtx.PluginStore, &datasources.FakeCacheService{},
&datasources.FakeDataSourceService{}, pluginSettings.ProvideService(db.InitTestDB(t), fakeSecrets.NewFakeSecretsService()), nil, &pCfg)
&datasources.FakeDataSourceService{}, pluginSettings.ProvideService(db.InitTestDB(t), fakeSecrets.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider())
srv := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.Cfg = cfg
+1 -1
View File
@@ -513,7 +513,7 @@ func pluginAssetScenario(t *testing.T, desc string, url string, urlPattern strin
pluginStore: pluginstore.New(pluginRegistry, &fakes.FakeLoader{}),
pluginFileStore: filestore.ProvideService(pluginRegistry),
log: log.NewNopLogger(),
pluginsCDNService: pluginscdn.ProvideService(&config.Cfg{
pluginsCDNService: pluginscdn.ProvideService(&config.PluginManagementCfg{
PluginsCDNURLTemplate: cfg.PluginsCDNURLTemplate,
PluginSettings: cfg.PluginSettings,
}),
+2 -3
View File
@@ -14,11 +14,10 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/tracing"
"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/services/datasources"
datafakes "github.com/grafana/grafana/pkg/services/datasources/fakes"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
"github.com/grafana/grafana/pkg/services/user"
@@ -62,7 +61,7 @@ func framesPassThroughService(t *testing.T, frames data.Frames) (data.Frames, er
{JSONData: plugins.JSONData{ID: "test"}},
}},
&datafakes.FakeCacheService{}, &datafakes.FakeDataSourceService{},
nil, pluginFakes.NewFakeLicensingService(), &config.Cfg{}),
nil, pluginconfig.NewFakePluginRequestConfigProvider()),
tracer: tracing.InitializeTracerForTest(),
metrics: newMetrics(nil),
}
+3 -4
View File
@@ -15,11 +15,10 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing"
"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/services/datasources"
datafakes "github.com/grafana/grafana/pkg/services/datasources/fakes"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
"github.com/grafana/grafana/pkg/services/user"
@@ -42,7 +41,7 @@ func TestService(t *testing.T) {
PluginList: []pluginstore.Plugin{
{JSONData: plugins.JSONData{ID: "test"}},
},
}, &datafakes.FakeCacheService{}, &datafakes.FakeDataSourceService{}, nil, fakes.NewFakeLicensingService(), &config.Cfg{})
}, &datafakes.FakeCacheService{}, &datafakes.FakeDataSourceService{}, nil, pluginconfig.NewFakePluginRequestConfigProvider())
s := Service{
cfg: setting.NewCfg(),
@@ -128,7 +127,7 @@ func TestDSQueryError(t *testing.T) {
PluginList: []pluginstore.Plugin{
{JSONData: plugins.JSONData{ID: "test"}},
},
}, &datafakes.FakeCacheService{}, &datafakes.FakeDataSourceService{}, nil, nil, &config.Cfg{})
}, &datafakes.FakeCacheService{}, &datafakes.FakeDataSourceService{}, nil, pluginconfig.NewFakePluginRequestConfigProvider())
s := Service{
cfg: setting.NewCfg(),
+23 -75
View File
@@ -1,16 +1,13 @@
package config
import (
"github.com/grafana/grafana-azure-sdk-go/azsettings"
"github.com/grafana/grafana/pkg/plugins/log"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/setting"
)
type Cfg struct {
log log.Logger
// PluginManagementCfg is the configuration for the plugin management system.
// It includes settings which are used to configure different components of plugin management.
type PluginManagementCfg struct {
DevMode bool
PluginsPath string
@@ -20,25 +17,6 @@ type Cfg struct {
DisablePlugins []string
ForwardHostEnvVars []string
// AWS Plugin Auth
AWSAllowedAuthProviders []string
AWSAssumeRoleEnabled bool
AWSExternalId string
AWSSessionDuration string
AWSListMetricsPageLimit string
AWSForwardSettingsPlugins []string
// Azure Cloud settings
Azure *azsettings.AzureSettings
AzureAuthEnabled bool
// Proxy Settings
ProxySettings setting.SecureSocksDSProxySettings
BuildVersion string // TODO Remove
LogDatasourceRequests bool
PluginsCDNURLTemplate string
Tracing Tracing
@@ -52,57 +30,27 @@ type Cfg struct {
AngularSupportEnabled bool
HideAngularDeprecation []string
ConcurrentQueryCount int
UserFacingDefaultError string
DataProxyRowLimit int64
SQLDatasourceMaxOpenConnsDefault int
SQLDatasourceMaxIdleConnsDefault int
SQLDatasourceMaxConnLifetimeDefault int
}
func NewCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSettings, pluginsAllowUnsigned []string,
awsAllowedAuthProviders []string, awsAssumeRoleEnabled bool, awsExternalId string, awsSessionDuration string, awsListMetricsPageLimit string, AWSForwardSettingsPlugins []string, azure *azsettings.AzureSettings, secureSocksDSProxy setting.SecureSocksDSProxySettings,
grafanaVersion string, logDatasourceRequests bool, pluginsCDNURLTemplate string, appURL string, appSubURL string, tracing Tracing, features featuremgmt.FeatureToggles, angularSupportEnabled bool,
grafanaComURL string, disablePlugins []string, hideAngularDeprecation []string, forwardHostEnvVars []string, concurrentQueryCount int, azureAuthEnabled bool,
userFacingDefaultError string, dataProxyRowLimit int64,
sqlDatasourceMaxOpenConnsDefault int, sqlDatasourceMaxIdleConnsDefault int, sqlDatasourceMaxConnLifetimeDefault int,
) *Cfg {
return &Cfg{
log: log.New("plugin.cfg"),
PluginsPath: pluginsPath,
BuildVersion: grafanaVersion,
DevMode: devMode,
PluginSettings: pluginSettings,
PluginsAllowUnsigned: pluginsAllowUnsigned,
DisablePlugins: disablePlugins,
AWSAllowedAuthProviders: awsAllowedAuthProviders,
AWSAssumeRoleEnabled: awsAssumeRoleEnabled,
AWSExternalId: awsExternalId,
AWSSessionDuration: awsSessionDuration,
AWSListMetricsPageLimit: awsListMetricsPageLimit,
AWSForwardSettingsPlugins: AWSForwardSettingsPlugins,
Azure: azure,
ProxySettings: secureSocksDSProxy,
LogDatasourceRequests: logDatasourceRequests,
PluginsCDNURLTemplate: pluginsCDNURLTemplate,
Tracing: tracing,
GrafanaComURL: grafanaComURL,
GrafanaAppURL: appURL,
GrafanaAppSubURL: appSubURL,
Features: features,
AngularSupportEnabled: angularSupportEnabled,
HideAngularDeprecation: hideAngularDeprecation,
ForwardHostEnvVars: forwardHostEnvVars,
ConcurrentQueryCount: concurrentQueryCount,
AzureAuthEnabled: azureAuthEnabled,
UserFacingDefaultError: userFacingDefaultError,
DataProxyRowLimit: dataProxyRowLimit,
SQLDatasourceMaxOpenConnsDefault: sqlDatasourceMaxOpenConnsDefault,
SQLDatasourceMaxIdleConnsDefault: sqlDatasourceMaxIdleConnsDefault,
SQLDatasourceMaxConnLifetimeDefault: sqlDatasourceMaxConnLifetimeDefault,
// NewPluginManagementCfg returns a new PluginManagementCfg.
func NewPluginManagementCfg(devMode bool, pluginsPath string, pluginSettings setting.PluginSettings, pluginsAllowUnsigned []string,
pluginsCDNURLTemplate string, appURL string, appSubURL string, tracing Tracing, features featuremgmt.FeatureToggles,
angularSupportEnabled bool, grafanaComURL string, disablePlugins []string, hideAngularDeprecation []string,
forwardHostEnvVars []string) *PluginManagementCfg {
return &PluginManagementCfg{
PluginsPath: pluginsPath,
DevMode: devMode,
PluginSettings: pluginSettings,
PluginsAllowUnsigned: pluginsAllowUnsigned,
DisablePlugins: disablePlugins,
PluginsCDNURLTemplate: pluginsCDNURLTemplate,
Tracing: tracing,
GrafanaComURL: grafanaComURL,
GrafanaAppURL: appURL,
GrafanaAppSubURL: appSubURL,
Features: features,
AngularSupportEnabled: angularSupportEnabled,
HideAngularDeprecation: hideAngularDeprecation,
ForwardHostEnvVars: forwardHostEnvVars,
}
}
+15 -320
View File
@@ -2,32 +2,14 @@ package envvars
import (
"context"
"fmt"
"os"
"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"
"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/services/featuremgmt"
)
const (
customConfigPrefix = "GF_PLUGIN"
)
// allowedHostEnvVarNames is the list of environment variables that can be passed from Grafana's process to the
// permittedHostEnvVarNames is the list of environment variables that can be passed from Grafana's process to the
// plugin's process
var allowedHostEnvVarNames = []string{
var permittedHostEnvVarNames = []string{
// Env vars used by net/http (Go stdlib) for http/https proxy
// https://github.com/golang/net/blob/fbaf41277f28102c36926d1368dafbe2b54b4c1d/http/httpproxy/proxy.go#L91-L93
"HTTP_PROXY",
@@ -39,286 +21,25 @@ var allowedHostEnvVarNames = []string{
}
type Provider interface {
Get(ctx context.Context, p *plugins.Plugin) []string
PluginEnvVars(ctx context.Context, p *plugins.Plugin) []string
}
type Service struct {
cfg *config.Cfg
license plugins.Licensing
type Service struct{}
func DefaultProvider() *Service {
return &Service{}
}
func NewProvider(cfg *config.Cfg, license plugins.Licensing) *Service {
return &Service{
cfg: cfg,
license: license,
}
func (s *Service) PluginEnvVars(_ context.Context, _ *plugins.Plugin) []string {
return PermittedHostEnvVars()
}
func (s *Service) Get(ctx context.Context, p *plugins.Plugin) []string {
hostEnv := []string{
fmt.Sprintf("GF_VERSION=%s", s.cfg.BuildVersion),
}
if s.license != nil {
hostEnv = append(
hostEnv,
fmt.Sprintf("GF_EDITION=%s", s.license.Edition()),
fmt.Sprintf("GF_ENTERPRISE_LICENSE_PATH=%s", s.license.Path()),
fmt.Sprintf("GF_ENTERPRISE_APP_URL=%s", s.license.AppURL()),
)
hostEnv = append(hostEnv, s.license.Environment()...)
}
if p.ExternalService != nil {
hostEnv = append(
hostEnv,
fmt.Sprintf("GF_APP_URL=%s", s.cfg.GrafanaAppURL),
fmt.Sprintf("GF_PLUGIN_APP_CLIENT_ID=%s", p.ExternalService.ClientID),
fmt.Sprintf("GF_PLUGIN_APP_CLIENT_SECRET=%s", p.ExternalService.ClientSecret),
)
if p.ExternalService.PrivateKey != "" {
hostEnv = append(hostEnv, fmt.Sprintf("GF_PLUGIN_APP_PRIVATE_KEY=%s", p.ExternalService.PrivateKey))
}
}
hostEnv = append(hostEnv, s.featureToggleEnableVar(ctx)...)
hostEnv = append(hostEnv, s.awsEnvVars()...)
hostEnv = append(hostEnv, s.secureSocksProxyEnvVars()...)
hostEnv = append(hostEnv, azsettings.WriteToEnvStr(s.cfg.Azure)...)
hostEnv = append(hostEnv, s.tracingEnvVars(p)...)
// 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 p.SkipHostEnvVars {
hostEnv = append(hostEnv, s.allowedHostEnvVars()...)
}
ev := getPluginSettings(p.ID, s.cfg).asEnvVar(customConfigPrefix, hostEnv...)
return ev
}
// GetConfigMap returns a map of configuration that should be passed in a plugin request.
//
//nolint:gocyclo
func (s *Service) GetConfigMap(ctx context.Context, pluginID string, _ *auth.ExternalService) 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)
}
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)
// TODO add support via plugin SDK
// if externalService != nil {
// m[oauthtokenretriever.AppURL] = s.cfg.GrafanaAppURL
// m[oauthtokenretriever.AppClientID] = externalService.ClientID
// m[oauthtokenretriever.AppClientSecret] = externalService.ClientSecret
// m[oauthtokenretriever.AppPrivateKey] = externalService.PrivateKey
// }
if s.cfg.Features != nil {
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
}
}
}
}
// TODO add support via plugin SDK
// ps := getPluginSettings(pluginID, s.cfg)
// for k, v := range ps {
// m[fmt.Sprintf("%s_%s", customConfigPrefix, strings.ToUpper(k))] = v
// }
return m
}
func (s *Service) tracingEnvVars(plugin *plugins.Plugin) []string {
pluginTracingEnabled := s.cfg.Features != nil && s.cfg.Features.IsEnabledGlobally(featuremgmt.FlagEnablePluginsTracingByDefault)
if v, exists := s.cfg.PluginSettings[plugin.ID]["tracing"]; exists && !pluginTracingEnabled {
pluginTracingEnabled = v == "true"
}
if !s.cfg.Tracing.IsEnabled() || !pluginTracingEnabled {
return nil
}
vars := []string{
fmt.Sprintf("GF_INSTANCE_OTLP_ADDRESS=%s", s.cfg.Tracing.OpenTelemetry.Address),
fmt.Sprintf("GF_INSTANCE_OTLP_PROPAGATION=%s", s.cfg.Tracing.OpenTelemetry.Propagation),
fmt.Sprintf("GF_INSTANCE_OTLP_SAMPLER_TYPE=%s", s.cfg.Tracing.OpenTelemetry.Sampler),
fmt.Sprintf("GF_INSTANCE_OTLP_SAMPLER_PARAM=%.6f", s.cfg.Tracing.OpenTelemetry.SamplerParam),
fmt.Sprintf("GF_INSTANCE_OTLP_SAMPLER_REMOTE_URL=%s", s.cfg.Tracing.OpenTelemetry.SamplerRemoteURL),
}
if plugin.Info.Version != "" {
vars = append(vars, fmt.Sprintf("GF_PLUGIN_VERSION=%s", plugin.Info.Version))
}
return vars
}
func (s *Service) featureToggleEnableVar(ctx context.Context) []string {
var variables []string // an array is used for consistency and keep the logic simpler for no features case
if s.cfg.Features == nil {
return variables
}
enabledFeatures := s.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, fmt.Sprintf("GF_INSTANCE_FEATURE_TOGGLES_ENABLE=%s", strings.Join(features, ",")))
}
return variables
}
func (s *Service) awsEnvVars() []string {
var variables []string
if !s.cfg.AWSAssumeRoleEnabled {
variables = append(variables, awsds.AssumeRoleEnabledEnvVarKeyName+"=false")
}
if len(s.cfg.AWSAllowedAuthProviders) > 0 {
variables = append(variables, awsds.AllowedAuthProvidersEnvVarKeyName+"="+strings.Join(s.cfg.AWSAllowedAuthProviders, ","))
}
if s.cfg.AWSExternalId != "" {
variables = append(variables, awsds.GrafanaAssumeRoleExternalIdKeyName+"="+s.cfg.AWSExternalId)
}
if s.cfg.AWSSessionDuration != "" {
variables = append(variables, awsds.SessionDurationEnvVarKeyName+"="+s.cfg.AWSSessionDuration)
}
if s.cfg.AWSListMetricsPageLimit != "" {
variables = append(variables, awsds.ListMetricsPageLimitKeyName+"="+s.cfg.AWSListMetricsPageLimit)
}
return variables
}
func (s *Service) secureSocksProxyEnvVars() []string {
if s.cfg.ProxySettings.Enabled {
return []string{
proxy.PluginSecureSocksProxyClientCert + "=" + s.cfg.ProxySettings.ClientCert,
proxy.PluginSecureSocksProxyClientKey + "=" + s.cfg.ProxySettings.ClientKey,
proxy.PluginSecureSocksProxyRootCACert + "=" + s.cfg.ProxySettings.RootCA,
proxy.PluginSecureSocksProxyProxyAddress + "=" + s.cfg.ProxySettings.ProxyAddress,
proxy.PluginSecureSocksProxyServerName + "=" + s.cfg.ProxySettings.ServerName,
proxy.PluginSecureSocksProxyEnabled + "=" + strconv.FormatBool(s.cfg.ProxySettings.Enabled),
proxy.PluginSecureSocksProxyAllowInsecure + "=" + strconv.FormatBool(s.cfg.ProxySettings.AllowInsecure),
}
}
return nil
}
// allowedHostEnvVars returns the variables that can be passed from Grafana's process
// PermittedHostEnvVars returns the variables that can be passed from Grafana's process
// (current process, also known as: "host") to the plugin process.
// A string in format "k=v" is returned for each variable in allowedHostEnvVarNames, if it's set.
func (s *Service) allowedHostEnvVars() []string {
// A string in format "k=v" is returned for each variable in PermittedHostEnvVarNames, if it's set.
func PermittedHostEnvVars() []string {
var r []string
for _, envVarName := range allowedHostEnvVarNames {
for _, envVarName := range PermittedHostEnvVarNames() {
if envVarValue, ok := os.LookupEnv(envVarName); ok {
r = append(r, envVarName+"="+envVarValue)
}
@@ -326,32 +47,6 @@ func (s *Service) allowedHostEnvVars() []string {
return r
}
type pluginSettings map[string]string
func getPluginSettings(pluginID string, cfg *config.Cfg) pluginSettings {
ps := pluginSettings{}
for k, v := range cfg.PluginSettings[pluginID] {
if k == "path" || strings.ToLower(k) == "id" {
continue
}
ps[k] = v
}
return ps
}
func (ps pluginSettings) asEnvVar(prefix string, hostEnv ...string) []string {
env := make([]string, 0, len(ps))
for k, v := range ps {
key := fmt.Sprintf("%s_%s", prefix, strings.ToUpper(k))
if value := os.Getenv(key); value != "" {
v = value
}
env = append(env, fmt.Sprintf("%s=%s", key, v))
}
env = append(env, hostEnv...)
return env
func PermittedHostEnvVarNames() []string {
return permittedHostEnvVarNames
}
+2 -2
View File
@@ -29,10 +29,10 @@ var (
type Service struct {
pluginRegistry registry.Service
cfg *config.Cfg
cfg *config.PluginManagementCfg
}
func ProvideService(pluginRegistry registry.Service, cfg *config.Cfg) *Service {
func ProvideService(pluginRegistry registry.Service, cfg *config.PluginManagementCfg) *Service {
return &Service{
pluginRegistry: pluginRegistry,
cfg: cfg,
+8 -8
View File
@@ -18,7 +18,7 @@ import (
func TestQueryData(t *testing.T) {
t.Run("Empty registry should return not registered error", func(t *testing.T) {
registry := fakes.NewFakePluginRegistry()
client := ProvideService(registry, &config.Cfg{})
client := ProvideService(registry, &config.PluginManagementCfg{})
_, err := client.QueryData(context.Background(), &backend.QueryDataRequest{})
require.Error(t, err)
require.ErrorIs(t, err, plugins.ErrPluginNotRegistered)
@@ -63,7 +63,7 @@ func TestQueryData(t *testing.T) {
err := registry.Add(context.Background(), p)
require.NoError(t, err)
client := ProvideService(registry, &config.Cfg{})
client := ProvideService(registry, &config.PluginManagementCfg{})
_, err = client.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{
PluginID: "grafana",
@@ -79,7 +79,7 @@ func TestQueryData(t *testing.T) {
func TestCheckHealth(t *testing.T) {
t.Run("empty plugin registry should return plugin not registered error", func(t *testing.T) {
registry := fakes.NewFakePluginRegistry()
client := ProvideService(registry, &config.Cfg{})
client := ProvideService(registry, &config.PluginManagementCfg{})
_, err := client.CheckHealth(context.Background(), &backend.CheckHealthRequest{})
require.Error(t, err)
require.ErrorIs(t, err, plugins.ErrPluginNotRegistered)
@@ -125,7 +125,7 @@ func TestCheckHealth(t *testing.T) {
err := registry.Add(context.Background(), p)
require.NoError(t, err)
client := ProvideService(registry, &config.Cfg{})
client := ProvideService(registry, &config.PluginManagementCfg{})
_, err = client.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{
PluginID: "grafana",
@@ -189,7 +189,7 @@ func TestCallResource(t *testing.T) {
err := registry.Add(context.Background(), p)
require.NoError(t, err)
client := ProvideService(registry, &config.Cfg{})
client := ProvideService(registry, &config.PluginManagementCfg{})
err = client.CallResource(context.Background(), req, sender)
require.NoError(t, err)
@@ -252,7 +252,7 @@ func TestCallResource(t *testing.T) {
err := registry.Add(context.Background(), p)
require.NoError(t, err)
client := ProvideService(registry, &config.Cfg{})
client := ProvideService(registry, &config.PluginManagementCfg{})
err = client.CallResource(context.Background(), req, sender)
require.NoError(t, err)
@@ -298,7 +298,7 @@ func TestCallResource(t *testing.T) {
err := registry.Add(context.Background(), p)
require.NoError(t, err)
client := ProvideService(registry, &config.Cfg{})
client := ProvideService(registry, &config.PluginManagementCfg{})
err = client.CallResource(context.Background(), req, sender)
require.NoError(t, err)
@@ -366,7 +366,7 @@ func TestCallResource(t *testing.T) {
err := registry.Add(context.Background(), p)
require.NoError(t, err)
client := ProvideService(registry, &config.Cfg{})
client := ProvideService(registry, &config.PluginManagementCfg{})
err = client.CallResource(context.Background(), req, sender)
require.NoError(t, err)
+15
View File
@@ -255,6 +255,21 @@ func (s *FakePluginStorage) Extract(ctx context.Context, pluginID string, dirNam
return &storage.ExtractedPluginArchive{}, nil
}
type FakePluginEnvProvider struct {
PluginEnvVarsFunc func(ctx context.Context, plugin *plugins.Plugin) []string
}
func NewFakePluginEnvProvider() *FakePluginEnvProvider {
return &FakePluginEnvProvider{}
}
func (p *FakePluginEnvProvider) PluginEnvVars(ctx context.Context, plugin *plugins.Plugin) []string {
if p.PluginEnvVarsFunc != nil {
return p.PluginEnvVarsFunc(ctx, plugin)
}
return []string{}
}
type FakeProcessManager struct {
StartFunc func(_ context.Context, p *plugins.Plugin) error
StopFunc func(_ context.Context, p *plugins.Plugin) error
+1 -1
View File
@@ -28,7 +28,7 @@ type PluginInstaller struct {
serviceRegistry auth.ExternalServiceRegistry
}
func ProvideInstaller(cfg *config.Cfg, pluginRegistry registry.Service, pluginLoader loader.Service,
func ProvideInstaller(cfg *config.PluginManagementCfg, pluginRegistry registry.Service, pluginLoader loader.Service,
pluginRepo repo.Service, serviceRegistry auth.ExternalServiceRegistry) *PluginInstaller {
return New(pluginRegistry, pluginLoader, pluginRepo,
storage.FileSystem(log.NewPrettyLogger("installer.fs"), cfg.PluginsPath), storage.SimpleDirNameGeneratorFunc, serviceRegistry)
@@ -18,10 +18,10 @@ import (
// on the plugins CDN, and it will switch to the correct implementation depending on the plugin and the config.
type Service struct {
cdn *pluginscdn.Service
cfg *config.Cfg
cfg *config.PluginManagementCfg
}
func ProvideService(cfg *config.Cfg, cdn *pluginscdn.Service) *Service {
func ProvideService(cfg *config.PluginManagementCfg, cdn *pluginscdn.Service) *Service {
return &Service{cfg: cfg, cdn: cdn}
}
@@ -39,7 +39,7 @@ func NewPluginInfo(pluginJSON plugins.JSONData, class plugins.Class, fs plugins.
}
}
func DefaultService(cfg *config.Cfg) *Service {
func DefaultService(cfg *config.PluginManagementCfg) *Service {
return &Service{cfg: cfg, cdn: pluginscdn.ProvideService(cfg)}
}
@@ -36,7 +36,7 @@ func TestService(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
cfg := &config.Cfg{
cfg := &config.PluginManagementCfg{
PluginsCDNURLTemplate: tc.cdnBaseURL,
PluginSettings: map[string]map[string]string{
"one": {"cdn": "true"},
@@ -142,7 +142,7 @@ func TestService(t *testing.T) {
appSubURL: "/grafana/",
},
} {
cfg := &config.Cfg{GrafanaAppSubURL: tc.appSubURL}
cfg := &config.PluginManagementCfg{GrafanaAppSubURL: tc.appSubURL}
svc := ProvideService(cfg, pluginscdn.ProvideService(cfg))
dir := "/plugins/test-datasource"
+1 -1
View File
@@ -37,7 +37,7 @@ func NewLocalFinder(devMode bool, features featuremgmt.FeatureToggles) *Local {
}
}
func ProvideLocalFinder(cfg *config.Cfg) *Local {
func ProvideLocalFinder(cfg *config.PluginManagementCfg) *Local {
return NewLocalFinder(cfg.DevMode, cfg.Features)
}
+13 -13
View File
@@ -61,14 +61,14 @@ func TestLoader_Load(t *testing.T) {
tests := []struct {
name string
class plugins.Class
cfg *config.Cfg
cfg *config.PluginManagementCfg
pluginPaths []string
want []*plugins.Plugin
}{
{
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, "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{"../testdata/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{"../testdata/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,14 +277,14 @@ 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{"../testdata/unsigned-datasource"},
want: []*plugins.Plugin{},
},
{
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(),
},
@@ -324,14 +324,14 @@ 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{"../testdata/lacking-files"},
want: []*plugins.Plugin{},
},
{
name: "Load a plugin with v1 manifest using PluginsAllowUnsigned config (production) should return signatureInvali",
class: plugins.ClassExternal,
cfg: &config.Cfg{
cfg: &config.PluginManagementCfg{
PluginsAllowUnsigned: []string{"test-datasource"},
Features: featuremgmt.WithFeatures(),
},
@@ -341,7 +341,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(),
},
@@ -351,7 +351,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(),
},
@@ -361,7 +361,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(),
},
@@ -413,7 +413,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(),
@@ -42,7 +42,7 @@ type Opts struct {
}
// New returns a new Bootstrap stage.
func New(cfg *config.Cfg, opts Opts) *Bootstrap {
func New(cfg *config.PluginManagementCfg, opts Opts) *Bootstrap {
if opts.ConstructFunc == nil {
opts.ConstructFunc = DefaultConstructFunc(signature.DefaultCalculator(cfg), assetpath.DefaultService(cfg))
}
@@ -29,7 +29,7 @@ func DefaultConstructFunc(signatureCalculator plugins.SignatureCalculator, asset
}
// DefaultDecorateFuncs are the default DecorateFuncs used for the Decorate step of the Bootstrap stage.
func DefaultDecorateFuncs(cfg *config.Cfg) []DecorateFunc {
func DefaultDecorateFuncs(cfg *config.PluginManagementCfg) []DecorateFunc {
return []DecorateFunc{
AppDefaultNavURLDecorateFunc,
TemplateDecorateFunc,
@@ -161,7 +161,7 @@ func configureAppChildPlugin(parent *plugins.Plugin, child *plugins.Plugin) {
// SkipHostEnvVarsDecorateFunc returns a DecorateFunc that configures the SkipHostEnvVars field of the plugin.
// It will be set to true if the FlagPluginsSkipHostEnvVars feature flag is set, and the plugin is not present in the
// ForwardHostEnvVars plugin ids list.
func SkipHostEnvVarsDecorateFunc(cfg *config.Cfg) DecorateFunc {
func SkipHostEnvVarsDecorateFunc(cfg *config.PluginManagementCfg) DecorateFunc {
return func(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
p.SkipHostEnvVars = cfg.Features.IsEnabledGlobally(featuremgmt.FlagPluginsSkipHostEnvVars) &&
!slices.Contains(cfg.ForwardHostEnvVars, p.ID)
@@ -144,7 +144,7 @@ func TestSkipEnvVarsDecorateFunc(t *testing.T) {
const pluginID = "plugin-id"
t.Run("feature flag is not present", func(t *testing.T) {
f := SkipHostEnvVarsDecorateFunc(&config.Cfg{Features: featuremgmt.WithFeatures()})
f := SkipHostEnvVarsDecorateFunc(&config.PluginManagementCfg{Features: featuremgmt.WithFeatures()})
p, err := f(context.Background(), &plugins.Plugin{JSONData: plugins.JSONData{ID: pluginID}})
require.NoError(t, err)
require.False(t, p.SkipHostEnvVars)
@@ -152,7 +152,7 @@ func TestSkipEnvVarsDecorateFunc(t *testing.T) {
t.Run("feature flag is present", func(t *testing.T) {
t.Run("no plugin settings should set SkipHostEnvVars to true", func(t *testing.T) {
f := SkipHostEnvVarsDecorateFunc(&config.Cfg{
f := SkipHostEnvVarsDecorateFunc(&config.PluginManagementCfg{
Features: featuremgmt.WithFeatures(featuremgmt.FlagPluginsSkipHostEnvVars),
})
p, err := f(context.Background(), &plugins.Plugin{JSONData: plugins.JSONData{ID: pluginID}})
@@ -188,7 +188,7 @@ func TestSkipEnvVarsDecorateFunc(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
f := SkipHostEnvVarsDecorateFunc(&config.Cfg{
f := SkipHostEnvVarsDecorateFunc(&config.PluginManagementCfg{
Features: featuremgmt.WithFeatures(featuremgmt.FlagPluginsSkipHostEnvVars),
ForwardHostEnvVars: tc.forwardHostEnvVars,
})
@@ -40,7 +40,7 @@ type Opts struct {
}
// New returns a new Discovery stage.
func New(cfg *config.Cfg, opts Opts) *Discovery {
func New(cfg *config.PluginManagementCfg, opts Opts) *Discovery {
if opts.FindFunc == nil {
opts.FindFunc = DefaultFindFunc(cfg)
}
@@ -11,7 +11,7 @@ import (
// DefaultFindFunc is the default function used for the Find step of the Discovery stage. It will scan the local
// filesystem for plugins.
func DefaultFindFunc(cfg *config.Cfg) FindFunc {
func DefaultFindFunc(cfg *config.PluginManagementCfg) FindFunc {
return finder.NewLocalFinder(cfg.DevMode, cfg.Features).Find
}
@@ -17,7 +17,7 @@ type Initializer interface {
type InitializeFunc func(ctx context.Context, p *plugins.Plugin) (*plugins.Plugin, error)
type Initialize struct {
cfg *config.Cfg
cfg *config.PluginManagementCfg
initializeSteps []InitializeFunc
log log.Logger
}
@@ -27,7 +27,7 @@ type Opts struct {
}
// New returns a new Initialization stage.
func New(cfg *config.Cfg, opts Opts) *Initialize {
func New(cfg *config.PluginManagementCfg, opts Opts) *Initialize {
if opts.InitializeFuncs == nil {
opts.InitializeFuncs = []InitializeFunc{}
}
@@ -47,7 +47,7 @@ func (b *BackendClientInit) Initialize(ctx context.Context, p *plugins.Plugin) (
}
// this will ensure that the env variables are calculated every time a plugin is started
envFunc := func() []string { return b.envVarProvider.Get(ctx, p) }
envFunc := func() []string { return b.envVarProvider.PluginEnvVars(ctx, p) }
if backendClient, err := backendFactory(p.ID, p.Logger(), envFunc); err != nil {
return nil, err
@@ -121,12 +121,12 @@ func (f *fakeBackendProvider) BackendFactory(_ context.Context, _ *plugins.Plugi
}
type fakeEnvVarsProvider struct {
GetFunc func(ctx context.Context, p *plugins.Plugin) []string
PluginEnvVarsFunc func(ctx context.Context, p *plugins.Plugin) []string
}
func (f *fakeEnvVarsProvider) Get(ctx context.Context, p *plugins.Plugin) []string {
if f.GetFunc != nil {
return f.GetFunc(ctx, p)
func (f *fakeEnvVarsProvider) PluginEnvVars(ctx context.Context, p *plugins.Plugin) []string {
if f.PluginEnvVarsFunc != nil {
return f.PluginEnvVars(ctx, p)
}
return nil
}
@@ -17,7 +17,7 @@ type Terminator interface {
type TerminateFunc func(ctx context.Context, p *plugins.Plugin) error
type Terminate struct {
cfg *config.Cfg
cfg *config.PluginManagementCfg
terminateSteps []TerminateFunc
log log.Logger
}
@@ -27,7 +27,7 @@ type Opts struct {
}
// New returns a new Termination stage.
func New(cfg *config.Cfg, opts Opts) (*Terminate, error) {
func New(cfg *config.PluginManagementCfg, opts Opts) (*Terminate, error) {
if opts.TerminateFuncs == nil {
opts.TerminateFuncs = []TerminateFunc{}
}
@@ -14,7 +14,7 @@ import (
)
// DefaultValidateFuncs are the default ValidateFunc used for the Validate step of the Validation stage.
func DefaultValidateFuncs(cfg *config.Cfg) []ValidateFunc {
func DefaultValidateFuncs(cfg *config.PluginManagementCfg) []ValidateFunc {
return []ValidateFunc{
SignatureValidationStep(signature.NewValidator(signature.NewUnsignedAuthorizer(cfg))),
ModuleJSValidationStep(),
@@ -74,16 +74,16 @@ func (v *ModuleJSValidator) Validate(_ context.Context, p *plugins.Plugin) error
}
type AngularDetector struct {
cfg *config.Cfg
cfg *config.PluginManagementCfg
angularInspector angularinspector.Inspector
log log.Logger
}
func AngularDetectionStep(cfg *config.Cfg, angularInspector angularinspector.Inspector) ValidateFunc {
func AngularDetectionStep(cfg *config.PluginManagementCfg, angularInspector angularinspector.Inspector) ValidateFunc {
return newAngularDetector(cfg, angularInspector).Validate
}
func newAngularDetector(cfg *config.Cfg, angularInspector angularinspector.Inspector) *AngularDetector {
func newAngularDetector(cfg *config.PluginManagementCfg, angularInspector angularinspector.Inspector) *AngularDetector {
return &AngularDetector{
cfg: cfg,
angularInspector: angularInspector,
@@ -17,7 +17,7 @@ type Validator interface {
type ValidateFunc func(ctx context.Context, p *plugins.Plugin) error
type Validate struct {
cfg *config.Cfg
cfg *config.PluginManagementCfg
validateSteps []ValidateFunc
log log.Logger
}
@@ -27,7 +27,7 @@ type Opts struct {
}
// New returns a new Validation stage.
func New(cfg *config.Cfg, opts Opts) *Validate {
func New(cfg *config.PluginManagementCfg, opts Opts) *Validate {
if opts.ValidateFuncs == nil {
opts.ValidateFuncs = DefaultValidateFuncs(cfg)
}
+3 -3
View File
@@ -5,18 +5,18 @@ import (
"github.com/grafana/grafana/pkg/plugins/config"
)
func ProvideOSSAuthorizer(cfg *config.Cfg) *UnsignedPluginAuthorizer {
func ProvideOSSAuthorizer(cfg *config.PluginManagementCfg) *UnsignedPluginAuthorizer {
return NewUnsignedAuthorizer(cfg)
}
func NewUnsignedAuthorizer(cfg *config.Cfg) *UnsignedPluginAuthorizer {
func NewUnsignedAuthorizer(cfg *config.PluginManagementCfg) *UnsignedPluginAuthorizer {
return &UnsignedPluginAuthorizer{
cfg: cfg,
}
}
type UnsignedPluginAuthorizer struct {
cfg *config.Cfg
cfg *config.PluginManagementCfg
}
func (u *UnsignedPluginAuthorizer) CanLoadPlugin(p *plugins.Plugin) bool {
+4 -4
View File
@@ -59,17 +59,17 @@ func (m *PluginManifest) isV2() bool {
type Signature struct {
kr plugins.KeyRetriever
cfg *config.Cfg
cfg *config.PluginManagementCfg
log log.Logger
}
var _ plugins.SignatureCalculator = &Signature{}
func ProvideService(cfg *config.Cfg, kr plugins.KeyRetriever) *Signature {
func ProvideService(cfg *config.PluginManagementCfg, kr plugins.KeyRetriever) *Signature {
return NewCalculator(cfg, kr)
}
func NewCalculator(cfg *config.Cfg, kr plugins.KeyRetriever) *Signature {
func NewCalculator(cfg *config.PluginManagementCfg, kr plugins.KeyRetriever) *Signature {
return &Signature{
kr: kr,
cfg: cfg,
@@ -77,7 +77,7 @@ func NewCalculator(cfg *config.Cfg, kr plugins.KeyRetriever) *Signature {
}
}
func DefaultCalculator(cfg *config.Cfg) *Signature {
func DefaultCalculator(cfg *config.PluginManagementCfg) *Signature {
return &Signature{
kr: statickey.New(),
cfg: cfg,
@@ -52,7 +52,7 @@ NR7DnB0CCQHO+4FlSPtXFTzNepoc+CytQyDAeOLMLmf2Tqhk2YShk+G/YlVX
-----END PGP SIGNATURE-----`
t.Run("valid manifest", func(t *testing.T) {
s := ProvideService(&config.Cfg{}, statickey.New())
s := ProvideService(&config.PluginManagementCfg{}, statickey.New())
manifest, err := s.readPluginManifest(context.Background(), []byte(txt))
require.NoError(t, err)
@@ -69,7 +69,7 @@ NR7DnB0CCQHO+4FlSPtXFTzNepoc+CytQyDAeOLMLmf2Tqhk2YShk+G/YlVX
t.Run("invalid manifest", func(t *testing.T) {
modified := strings.ReplaceAll(txt, "README.md", "xxxxxxxxxx")
s := ProvideService(&config.Cfg{}, statickey.New())
s := ProvideService(&config.PluginManagementCfg{}, statickey.New())
_, err := s.readPluginManifest(context.Background(), []byte(modified))
require.Error(t, err)
})
@@ -107,7 +107,7 @@ khdr/tZ1PDgRxMqB/u+Vtbpl0xSxgblnrDOYMSI=
-----END PGP SIGNATURE-----`
t.Run("valid manifest", func(t *testing.T) {
s := ProvideService(&config.Cfg{}, statickey.New())
s := ProvideService(&config.PluginManagementCfg{}, statickey.New())
manifest, err := s.readPluginManifest(context.Background(), []byte(txt))
require.NoError(t, err)
@@ -155,7 +155,7 @@ func TestCalculate(t *testing.T) {
for _, tc := range tcs {
basePath := filepath.Join(parentDir, "testdata/non-pvt-with-root-url/plugin")
s := ProvideService(&config.Cfg{GrafanaAppURL: tc.appURL}, statickey.New())
s := ProvideService(&config.PluginManagementCfg{GrafanaAppURL: tc.appURL}, statickey.New())
sig, err := s.Calculate(context.Background(), &fakes.FakePluginSource{
PluginClassFunc: func(ctx context.Context) plugins.Class {
return plugins.ClassExternal
@@ -183,7 +183,7 @@ func TestCalculate(t *testing.T) {
basePath := "../testdata/renderer-added-file/plugin"
runningWindows = true
s := ProvideService(&config.Cfg{}, statickey.New())
s := ProvideService(&config.PluginManagementCfg{}, statickey.New())
sig, err := s.Calculate(context.Background(), &fakes.FakePluginSource{
PluginClassFunc: func(ctx context.Context) plugins.Class {
return plugins.ClassExternal
@@ -247,7 +247,7 @@ func TestCalculate(t *testing.T) {
toSlash = tc.platform.toSlashFunc()
fromSlash = tc.platform.fromSlashFunc()
s := ProvideService(&config.Cfg{}, statickey.New())
s := ProvideService(&config.PluginManagementCfg{}, statickey.New())
pfs, err := tc.fsFactory()
require.NoError(t, err)
pfs, err = newPathSeparatorOverrideFS(string(tc.platform.separator), pfs)
@@ -715,7 +715,7 @@ func Test_validateManifest(t *testing.T) {
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
s := ProvideService(&config.Cfg{}, statickey.New())
s := ProvideService(&config.PluginManagementCfg{}, statickey.New())
err := s.validateManifest(context.Background(), *tc.manifest, nil)
require.Errorf(t, err, tc.expectedErr)
})
@@ -811,7 +811,7 @@ pHo=
}
func Test_VerifyRevokedKey(t *testing.T) {
s := ProvideService(&config.Cfg{}, &revokedKeyProvider{})
s := ProvideService(&config.PluginManagementCfg{}, &revokedKeyProvider{})
m := createV2Manifest(t)
txt := `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
+2 -2
View File
@@ -16,10 +16,10 @@ var ErrPluginNotCDN = errors.New("plugin is not a cdn plugin")
// Service provides methods for the plugins CDN.
type Service struct {
cfg *config.Cfg
cfg *config.PluginManagementCfg
}
func ProvideService(cfg *config.Cfg) *Service {
func ProvideService(cfg *config.PluginManagementCfg) *Service {
return &Service{cfg: cfg}
}
+2 -2
View File
@@ -8,7 +8,7 @@ import (
)
func TestService(t *testing.T) {
svc := ProvideService(&config.Cfg{
svc := ProvideService(&config.PluginManagementCfg{
PluginsCDNURLTemplate: "https://cdn.example.com",
PluginSettings: map[string]map[string]string{
"one": {"cdn": "true"},
@@ -40,7 +40,7 @@ func TestService(t *testing.T) {
},
} {
t.Run(c.name, func(t *testing.T) {
u, err := ProvideService(&config.Cfg{PluginsCDNURLTemplate: c.cfgURL}).BaseURL()
u, err := ProvideService(&config.PluginManagementCfg{PluginsCDNURLTemplate: c.cfgURL}).BaseURL()
require.NoError(t, err)
require.Equal(t, c.expBaseURL, u)
})
+1 -1
View File
@@ -21,7 +21,7 @@ type Manager struct {
log log.PrettyLogger
}
func ProvideService(cfg *config.Cfg) (*Manager, error) {
func ProvideService(cfg *config.PluginManagementCfg) (*Manager, error) {
baseURL, err := url.JoinPath(cfg.GrafanaComURL, "/api/plugins")
if err != nil {
return nil, err
@@ -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
}
@@ -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,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,4 +1,4 @@
package config
package pluginconfig
import (
"fmt"
@@ -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 {
@@ -16,8 +16,6 @@ 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/manager/fakes"
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
@@ -27,6 +25,7 @@ import (
datasourceService "github.com/grafana/grafana/pkg/services/datasources/service"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/licensing/licensingtest"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
@@ -150,8 +149,7 @@ func buildQueryDataService(t *testing.T, cs datasources.CacheService, fpc *fakeP
},
},
}, &fakeDatasources.FakeCacheService{}, ds,
pluginSettings.ProvideService(store, fakeSecrets.NewFakeSecretsService()), fakes.NewFakeLicensingService(),
&config.Cfg{})
pluginSettings.ProvideService(store, fakeSecrets.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider())
return query.ProvideService(
setting.NewCfg(),
+2 -3
View File
@@ -24,8 +24,6 @@ import (
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/models/roletype"
"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/services/auth/identity"
"github.com/grafana/grafana/pkg/services/contexthandler"
"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey"
@@ -33,6 +31,7 @@ import (
"github.com/grafana/grafana/pkg/services/datasources"
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
@@ -482,7 +481,7 @@ func setup(t *testing.T) *testContext {
{JSONData: plugins.JSONData{ID: "mysql"}},
},
}, &fakeDatasources.FakeCacheService{}, fakeDatasourceService,
pluginSettings.ProvideService(sqlStore, secretsService), pluginFakes.NewFakeLicensingService(), &config.Cfg{},
pluginSettings.ProvideService(sqlStore, secretsService), pluginconfig.NewFakePluginRequestConfigProvider(),
)
exprService := expr.ProvideService(&setting.Cfg{ExpressionsEnabled: true}, pc, pCtxProvider,
&featuremgmt.FeatureManager{}, nil, tracing.InitializeTracerForTest())
+2 -3
View File
@@ -12,13 +12,12 @@ 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"
pluginFakes "github.com/grafana/grafana/pkg/plugins/manager/fakes"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/datasources/guardian"
datasourceservice "github.com/grafana/grafana/pkg/services/datasources/service"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
@@ -54,7 +53,7 @@ func TestHandleRequest(t *testing.T) {
pCtxProvider := plugincontext.ProvideService(sqlStore.Cfg, localcache.ProvideService(), &pluginstore.FakePluginStore{
PluginList: []pluginstore.Plugin{{JSONData: plugins.JSONData{ID: "test"}}},
}, dsCache, dsService, pluginSettings.ProvideService(sqlStore, secretsService), pluginFakes.NewFakeLicensingService(), &config.Cfg{})
}, dsCache, dsService, pluginSettings.ProvideService(sqlStore, secretsService), pluginconfig.NewFakePluginRequestConfigProvider())
s := ProvideService(client, nil, dsService, pCtxProvider)
ds := &datasources.DataSource{ID: 12, Type: "test", JsonData: simplejson.New()}