Backend Plugins: Plugin configuration using Grafana config (#23451)
Enables adding a section `plugin.<plugin id>` and key/value to Grafana configuration file which will be converted and sent as environment variables to the backend plugin. Also sends some additional environment variables, Grafana version (GF_VERSION), Grafana edition (GF_EDITION) and enterprise license path (GF_ENTERPRISE_LICENSE_PATH). Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Fixes #21515,
This commit is contained in:
co-authored by
Arve Knudsen
parent
941cd59894
commit
34266cd369
@@ -30,9 +30,12 @@ var handshake = goplugin.HandshakeConfig{
|
||||
MagicCookieValue: grpcplugin.MagicCookieValue,
|
||||
}
|
||||
|
||||
func newClientConfig(executablePath string, logger log.Logger, versionedPlugins map[int]goplugin.PluginSet) *goplugin.ClientConfig {
|
||||
func newClientConfig(executablePath string, env []string, logger log.Logger, versionedPlugins map[int]goplugin.PluginSet) *goplugin.ClientConfig {
|
||||
cmd := exec.Command(executablePath)
|
||||
cmd.Env = env
|
||||
|
||||
return &goplugin.ClientConfig{
|
||||
Cmd: exec.Command(executablePath),
|
||||
Cmd: cmd,
|
||||
HandshakeConfig: handshake,
|
||||
VersionedPlugins: versionedPlugins,
|
||||
Logger: logWrapper{Logger: logger},
|
||||
|
||||
@@ -3,11 +3,13 @@ package backendplugin
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
"github.com/grafana/grafana/pkg/util/proxyutil"
|
||||
|
||||
@@ -49,14 +51,18 @@ type Manager interface {
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
pluginsMu sync.RWMutex
|
||||
plugins map[string]*BackendPlugin
|
||||
logger log.Logger
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
License models.Licensing `inject:""`
|
||||
pluginsMu sync.RWMutex
|
||||
plugins map[string]*BackendPlugin
|
||||
logger log.Logger
|
||||
pluginSettings map[string]pluginSettings
|
||||
}
|
||||
|
||||
func (m *manager) Init() error {
|
||||
m.plugins = make(map[string]*BackendPlugin)
|
||||
m.logger = log.New("plugins.backend")
|
||||
m.pluginSettings = extractPluginSettings(m.Cfg)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -78,13 +84,29 @@ func (m *manager) Register(descriptor PluginDescriptor) error {
|
||||
return errors.New("Backend plugin already registered")
|
||||
}
|
||||
|
||||
pluginSettings := pluginSettings{}
|
||||
if ps, exists := m.pluginSettings[descriptor.pluginID]; exists {
|
||||
pluginSettings = ps
|
||||
}
|
||||
|
||||
hostEnv := []string{
|
||||
fmt.Sprintf("GF_VERSION=%s", setting.BuildVersion),
|
||||
fmt.Sprintf("GF_EDITION=%s", m.License.Edition()),
|
||||
}
|
||||
|
||||
if m.License.HasLicense() {
|
||||
hostEnv = append(hostEnv, fmt.Sprintf("GF_ENTERPRISE_LICENSE_PATH=%s", m.Cfg.EnterpriseLicensePath))
|
||||
}
|
||||
|
||||
env := pluginSettings.ToEnv("GF_PLUGIN", hostEnv)
|
||||
|
||||
pluginLogger := m.logger.New("pluginId", descriptor.pluginID)
|
||||
plugin := &BackendPlugin{
|
||||
id: descriptor.pluginID,
|
||||
executablePath: descriptor.executablePath,
|
||||
managed: descriptor.managed,
|
||||
clientFactory: func() *plugin.Client {
|
||||
return plugin.NewClient(newClientConfig(descriptor.executablePath, pluginLogger, descriptor.versionedPlugins))
|
||||
return plugin.NewClient(newClientConfig(descriptor.executablePath, env, pluginLogger, descriptor.versionedPlugins))
|
||||
},
|
||||
startFns: descriptor.startFns,
|
||||
logger: pluginLogger,
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package backendplugin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type pluginSettings map[string]string
|
||||
|
||||
func (ps pluginSettings) ToEnv(prefix string, hostEnv []string) []string {
|
||||
env := []string{}
|
||||
for k, v := range ps {
|
||||
env = append(env, fmt.Sprintf("%s_%s=%s", prefix, strings.ToUpper(k), v))
|
||||
}
|
||||
|
||||
env = append(env, hostEnv...)
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
func extractPluginSettings(cfg *setting.Cfg) map[string]pluginSettings {
|
||||
psMap := map[string]pluginSettings{}
|
||||
for pluginID, settings := range cfg.PluginSettings {
|
||||
ps := pluginSettings{}
|
||||
for k, v := range settings {
|
||||
if k == "path" || strings.ToLower(k) == "id" {
|
||||
continue
|
||||
}
|
||||
|
||||
ps[k] = v
|
||||
}
|
||||
|
||||
psMap[pluginID] = ps
|
||||
}
|
||||
|
||||
return psMap
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package backendplugin
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPluginSettings(t *testing.T) {
|
||||
t.Run("Should only extract from sections beginning with 'plugin.' in config", func(t *testing.T) {
|
||||
cfg := &setting.Cfg{
|
||||
PluginSettings: setting.PluginSettings{
|
||||
"plugin": map[string]string{
|
||||
"key1": "value1",
|
||||
"key2": "value2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ps := extractPluginSettings(cfg)
|
||||
require.Len(t, ps, 1)
|
||||
require.Len(t, ps["plugin"], 2)
|
||||
|
||||
t.Run("Should skip path setting", func(t *testing.T) {
|
||||
cfg.PluginSettings["plugin"]["path"] = "value"
|
||||
ps := extractPluginSettings(cfg)
|
||||
require.Len(t, ps["plugin"], 2)
|
||||
})
|
||||
|
||||
t.Run("Should skip id setting", func(t *testing.T) {
|
||||
cfg.PluginSettings["plugin"]["id"] = "value"
|
||||
ps := extractPluginSettings(cfg)
|
||||
require.Len(t, ps["plugin"], 2)
|
||||
})
|
||||
|
||||
t.Run("Should return expected environment variables from plugin settings ", func(t *testing.T) {
|
||||
ps := extractPluginSettings(cfg)
|
||||
env := ps["plugin"].ToEnv("GF_PLUGIN", []string{"GF_VERSION=6.7.0"})
|
||||
sort.Strings(env)
|
||||
require.Len(t, env, 3)
|
||||
require.EqualValues(t, []string{"GF_PLUGIN_KEY1=value1", "GF_PLUGIN_KEY2=value2", "GF_VERSION=6.7.0"}, env)
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user