mirror of https://github.com/grafana/grafana
Plugins: Refactor env vars to dedicated service (#68960)
* add env vars service * fix tests * fix more testspull/69095/head
parent
eff90809a6
commit
ae373c662c
@ -0,0 +1,132 @@ |
||||
package envvars |
||||
|
||||
import ( |
||||
"context" |
||||
"fmt" |
||||
"os" |
||||
"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/config" |
||||
) |
||||
|
||||
type Provider interface { |
||||
Get(ctx context.Context, p *plugins.Plugin) []string |
||||
} |
||||
|
||||
type Service struct { |
||||
cfg *config.Cfg |
||||
license plugins.Licensing |
||||
} |
||||
|
||||
func NewProvider(cfg *config.Cfg, license plugins.Licensing) *Service { |
||||
return &Service{ |
||||
cfg: cfg, |
||||
license: license, |
||||
} |
||||
} |
||||
|
||||
func (s *Service) Get(_ 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()...) |
||||
} |
||||
|
||||
hostEnv = append(hostEnv, s.awsEnvVars()...) |
||||
hostEnv = append(hostEnv, s.secureSocksProxyEnvVars()...) |
||||
hostEnv = append(hostEnv, azsettings.WriteToEnvStr(s.cfg.Azure)...) |
||||
hostEnv = append(hostEnv, s.tracingEnvVars(p)...) |
||||
|
||||
ev := getPluginSettings(p.ID, s.cfg).asEnvVar("GF_PLUGIN", hostEnv) |
||||
return ev |
||||
} |
||||
|
||||
func (s *Service) tracingEnvVars(plugin *plugins.Plugin) []string { |
||||
var pluginTracingEnabled bool |
||||
if v, exists := s.cfg.PluginSettings[plugin.ID]["tracing"]; exists { |
||||
pluginTracingEnabled = v == "true" |
||||
} |
||||
if !s.cfg.Tracing.IsEnabled() || !pluginTracingEnabled { |
||||
return nil |
||||
} |
||||
|
||||
var vars []string |
||||
if plugin.Info.Version != "" { |
||||
vars = append(vars, fmt.Sprintf("GF_PLUGIN_VERSION=%s", plugin.Info.Version)) |
||||
} |
||||
return append( |
||||
vars, |
||||
fmt.Sprintf("GF_INSTANCE_OTLP_ADDRESS=%s", s.cfg.Tracing.OpenTelemetry.Address), |
||||
fmt.Sprintf("GF_INSTANCE_OTLP_PROPAGATION=%s", s.cfg.Tracing.OpenTelemetry.Propagation), |
||||
) |
||||
} |
||||
|
||||
func (s *Service) awsEnvVars() []string { |
||||
var variables []string |
||||
if s.cfg.AWSAssumeRoleEnabled { |
||||
variables = append(variables, awsds.AssumeRoleEnabledEnvVarKeyName+"=true") |
||||
} |
||||
if len(s.cfg.AWSAllowedAuthProviders) > 0 { |
||||
variables = append(variables, awsds.AllowedAuthProvidersEnvVarKeyName+"="+strings.Join(s.cfg.AWSAllowedAuthProviders, ",")) |
||||
} |
||||
|
||||
return variables |
||||
} |
||||
|
||||
func (s *Service) secureSocksProxyEnvVars() []string { |
||||
var variables []string |
||||
if s.cfg.ProxySettings.Enabled { |
||||
variables = append(variables, proxy.PluginSecureSocksProxyClientCert+"="+s.cfg.ProxySettings.ClientCert) |
||||
variables = append(variables, proxy.PluginSecureSocksProxyClientKey+"="+s.cfg.ProxySettings.ClientKey) |
||||
variables = append(variables, proxy.PluginSecureSocksProxyRootCACert+"="+s.cfg.ProxySettings.RootCA) |
||||
variables = append(variables, proxy.PluginSecureSocksProxyProxyAddress+"="+s.cfg.ProxySettings.ProxyAddress) |
||||
variables = append(variables, proxy.PluginSecureSocksProxyServerName+"="+s.cfg.ProxySettings.ServerName) |
||||
variables = append(variables, proxy.PluginSecureSocksProxyEnabled+"="+strconv.FormatBool(s.cfg.ProxySettings.Enabled)) |
||||
} |
||||
|
||||
return variables |
||||
} |
||||
|
||||
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 |
||||
} |
||||
@ -0,0 +1,304 @@ |
||||
package envvars |
||||
|
||||
import ( |
||||
"context" |
||||
"strings" |
||||
"testing" |
||||
|
||||
"github.com/stretchr/testify/assert" |
||||
"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 TestInitializer_envVars(t *testing.T) { |
||||
t.Run("backend datasource with license", func(t *testing.T) { |
||||
p := &plugins.Plugin{ |
||||
JSONData: plugins.JSONData{ |
||||
ID: "test", |
||||
}, |
||||
} |
||||
|
||||
licensing := &fakes.FakeLicensingService{ |
||||
LicenseEdition: "test", |
||||
TokenRaw: "token", |
||||
LicensePath: "/path/to/ent/license", |
||||
LicenseAppURL: "https://myorg.com/", |
||||
} |
||||
|
||||
envVarsProvider := NewProvider(&config.Cfg{ |
||||
PluginSettings: map[string]map[string]string{ |
||||
"test": { |
||||
"custom_env_var": "customVal", |
||||
}, |
||||
}, |
||||
}, licensing) |
||||
|
||||
envVars := envVarsProvider.Get(context.Background(), p) |
||||
assert.Len(t, envVars, 6) |
||||
assert.Equal(t, "GF_PLUGIN_CUSTOM_ENV_VAR=customVal", envVars[0]) |
||||
assert.Equal(t, "GF_VERSION=", envVars[1]) |
||||
assert.Equal(t, "GF_EDITION=test", envVars[2]) |
||||
assert.Equal(t, "GF_ENTERPRISE_LICENSE_PATH=/path/to/ent/license", envVars[3]) |
||||
assert.Equal(t, "GF_ENTERPRISE_APP_URL=https://myorg.com/", envVars[4]) |
||||
assert.Equal(t, "GF_ENTERPRISE_LICENSE_TEXT=token", envVars[5]) |
||||
}) |
||||
} |
||||
|
||||
func TestInitializer_tracingEnvironmentVariables(t *testing.T) { |
||||
const pluginID = "plugin_id" |
||||
|
||||
defaultPlugin := &plugins.Plugin{ |
||||
JSONData: plugins.JSONData{ |
||||
ID: pluginID, |
||||
Info: plugins.Info{Version: "1.0.0"}, |
||||
}, |
||||
} |
||||
pluginWithoutVersion := &plugins.Plugin{ |
||||
JSONData: plugins.JSONData{ID: pluginID}, |
||||
} |
||||
|
||||
defaultOTelCfg := config.OpenTelemetryCfg{ |
||||
Address: "127.0.0.1:4317", |
||||
Propagation: "", |
||||
} |
||||
|
||||
expDefaultOtlp := func(t *testing.T, envVars []string) { |
||||
found := map[string]bool{ |
||||
"address": false, |
||||
"plugin_version": false, |
||||
"propagation": false, |
||||
} |
||||
setFound := func(v string) { |
||||
require.False(t, found[v], "duplicate env var found") |
||||
found[v] = true |
||||
} |
||||
for _, v := range envVars { |
||||
switch v { |
||||
case "GF_PLUGIN_VERSION=1.0.0": |
||||
setFound("plugin_version") |
||||
case "GF_INSTANCE_OTLP_ADDRESS=127.0.0.1:4317": |
||||
setFound("address") |
||||
case "GF_INSTANCE_OTLP_PROPAGATION=": |
||||
setFound("propagation") |
||||
} |
||||
} |
||||
for k, f := range found { |
||||
require.Truef(t, f, "%q env var not found: %+v", k, envVars) |
||||
} |
||||
} |
||||
expNoTracing := func(t *testing.T, envVars []string) { |
||||
for _, v := range envVars { |
||||
assert.False(t, strings.HasPrefix(v, "GF_TRACING"), "should not have tracing env var") |
||||
assert.False( |
||||
t, |
||||
strings.HasPrefix(v, "GF_PLUGIN_VERSION"), |
||||
"GF_PLUGIN_VERSION is tracing-only and should not be present when tracing is disabled", |
||||
) |
||||
} |
||||
} |
||||
expGfPluginVersionNotPresent := func(t *testing.T, envVars []string) { |
||||
for _, e := range envVars { |
||||
assert.False(t, strings.HasPrefix("GF_PLUGIN_VERSION=", e), "GF_PLUGIN_VERSION shouldn't be present") |
||||
} |
||||
} |
||||
expGfPluginVersionPresent := func(t *testing.T, envVars []string) { |
||||
var found bool |
||||
for _, e := range envVars { |
||||
if e != "GF_PLUGIN_VERSION=1.0.0" { |
||||
continue |
||||
} |
||||
assert.False(t, found, "GF_PLUGIN_VERSION is present multiple times") |
||||
found = true |
||||
} |
||||
assert.Truef(t, found, "GF_PLUGIN_VERSION is not present: %+v", envVars) |
||||
} |
||||
|
||||
for _, tc := range []struct { |
||||
name string |
||||
cfg *config.Cfg |
||||
plugin *plugins.Plugin |
||||
exp func(t *testing.T, envVars []string) |
||||
}{ |
||||
{ |
||||
name: "otel not configured", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expNoTracing, |
||||
}, |
||||
{ |
||||
name: "otel not configured but plugin-tracing enabled", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{}, |
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expNoTracing, |
||||
}, |
||||
{ |
||||
name: "otlp no propagation plugin enabled", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: defaultOTelCfg, |
||||
}, |
||||
PluginSettings: map[string]map[string]string{ |
||||
pluginID: {"tracing": "true"}, |
||||
}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expDefaultOtlp, |
||||
}, |
||||
{ |
||||
name: "otlp no propagation disabled by default", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: defaultOTelCfg, |
||||
}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expNoTracing, |
||||
}, |
||||
{ |
||||
name: "otlp propagation plugin enabled", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: config.OpenTelemetryCfg{ |
||||
Address: "127.0.0.1:4317", |
||||
Propagation: "w3c", |
||||
}, |
||||
}, |
||||
PluginSettings: map[string]map[string]string{ |
||||
pluginID: {"tracing": "true"}, |
||||
}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: func(t *testing.T, envVars []string) { |
||||
assert.Len(t, envVars, 5) |
||||
assert.Equal(t, "GF_PLUGIN_TRACING=true", envVars[0]) |
||||
assert.Equal(t, "GF_VERSION=", envVars[1]) |
||||
assert.Equal(t, "GF_PLUGIN_VERSION=1.0.0", envVars[2]) |
||||
assert.Equal(t, "GF_INSTANCE_OTLP_ADDRESS=127.0.0.1:4317", envVars[3]) |
||||
assert.Equal(t, "GF_INSTANCE_OTLP_PROPAGATION=w3c", envVars[4]) |
||||
}, |
||||
}, |
||||
{ |
||||
name: "otlp enabled composite propagation", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: config.OpenTelemetryCfg{ |
||||
Address: "127.0.0.1:4317", |
||||
Propagation: "w3c,jaeger", |
||||
}, |
||||
}, |
||||
PluginSettings: map[string]map[string]string{ |
||||
pluginID: {"tracing": "true"}, |
||||
}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: func(t *testing.T, envVars []string) { |
||||
assert.Len(t, envVars, 5) |
||||
assert.Equal(t, "GF_PLUGIN_TRACING=true", envVars[0]) |
||||
assert.Equal(t, "GF_VERSION=", envVars[1]) |
||||
assert.Equal(t, "GF_PLUGIN_VERSION=1.0.0", envVars[2]) |
||||
assert.Equal(t, "GF_INSTANCE_OTLP_ADDRESS=127.0.0.1:4317", envVars[3]) |
||||
assert.Equal(t, "GF_INSTANCE_OTLP_PROPAGATION=w3c,jaeger", envVars[4]) |
||||
}, |
||||
}, |
||||
{ |
||||
name: "otlp no propagation disabled by default", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: config.OpenTelemetryCfg{ |
||||
Address: "127.0.0.1:4317", |
||||
Propagation: "w3c", |
||||
}, |
||||
}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expNoTracing, |
||||
}, |
||||
{ |
||||
name: "disabled on plugin", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: defaultOTelCfg, |
||||
}, |
||||
PluginSettings: setting.PluginSettings{ |
||||
pluginID: map[string]string{"tracing": "false"}, |
||||
}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expNoTracing, |
||||
}, |
||||
{ |
||||
name: "disabled on plugin with other plugin settings", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: defaultOTelCfg, |
||||
}, |
||||
PluginSettings: map[string]map[string]string{ |
||||
pluginID: {"some_other_option": "true"}, |
||||
}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expNoTracing, |
||||
}, |
||||
{ |
||||
name: "enabled on plugin with other plugin settings", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: defaultOTelCfg, |
||||
}, |
||||
PluginSettings: map[string]map[string]string{ |
||||
pluginID: {"some_other_option": "true", "tracing": "true"}, |
||||
}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expDefaultOtlp, |
||||
}, |
||||
{ |
||||
name: "GF_PLUGIN_VERSION is not present if tracing is disabled", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: config.OpenTelemetryCfg{}, |
||||
}, |
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expGfPluginVersionNotPresent, |
||||
}, |
||||
{ |
||||
name: "GF_PLUGIN_VERSION is present if tracing is enabled and plugin has version", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: defaultOTelCfg, |
||||
}, |
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}}, |
||||
}, |
||||
plugin: defaultPlugin, |
||||
exp: expGfPluginVersionPresent, |
||||
}, |
||||
{ |
||||
name: "GF_PLUGIN_VERSION is not present if tracing is enabled but plugin doesn't have a version", |
||||
cfg: &config.Cfg{ |
||||
Tracing: config.Tracing{ |
||||
OpenTelemetry: config.OpenTelemetryCfg{}, |
||||
}, |
||||
PluginSettings: map[string]map[string]string{pluginID: {"tracing": "true"}}, |
||||
}, |
||||
plugin: pluginWithoutVersion, |
||||
exp: expGfPluginVersionNotPresent, |
||||
}, |
||||
} { |
||||
t.Run(tc.name, func(t *testing.T) { |
||||
envVarsProvider := NewProvider(tc.cfg, nil) |
||||
envVars := envVarsProvider.Get(context.Background(), tc.plugin) |
||||
tc.exp(t, envVars) |
||||
}) |
||||
} |
||||
} |
||||
Loading…
Reference in new issue