wire up loading of secretsmanager plugin, using renderer plugin as a model
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
|
||||
goplugin "github.com/hashicorp/go-plugin"
|
||||
)
|
||||
|
||||
@@ -41,23 +42,28 @@ func newClientConfig(executablePath string, env []string, logger log.Logger,
|
||||
// StartRendererFunc callback function called when a renderer plugin is started.
|
||||
type StartRendererFunc func(pluginID string, renderer pluginextensionv2.RendererPlugin, logger log.Logger) error
|
||||
|
||||
// StartSecretsManagerFunc callback function called when a secrets manager plugin is started.
|
||||
type StartSecretsManagerFunc func(pluginID string, secretsmanager secretsmanagerplugin.SecretsManagerPlugin, logger log.Logger) error
|
||||
|
||||
// PluginDescriptor is a descriptor used for registering backend plugins.
|
||||
type PluginDescriptor struct {
|
||||
pluginID string
|
||||
executablePath string
|
||||
managed bool
|
||||
versionedPlugins map[int]goplugin.PluginSet
|
||||
startRendererFn StartRendererFunc
|
||||
pluginID string
|
||||
executablePath string
|
||||
managed bool
|
||||
versionedPlugins map[int]goplugin.PluginSet
|
||||
startRendererFn StartRendererFunc
|
||||
startSecretsmanagerFn StartSecretsManagerFunc
|
||||
}
|
||||
|
||||
// getV2PluginSet returns list of plugins supported on v2.
|
||||
func getV2PluginSet() goplugin.PluginSet {
|
||||
return goplugin.PluginSet{
|
||||
"diagnostics": &grpcplugin.DiagnosticsGRPCPlugin{},
|
||||
"resource": &grpcplugin.ResourceGRPCPlugin{},
|
||||
"data": &grpcplugin.DataGRPCPlugin{},
|
||||
"stream": &grpcplugin.StreamGRPCPlugin{},
|
||||
"renderer": &pluginextensionv2.RendererGRPCPlugin{},
|
||||
"diagnostics": &grpcplugin.DiagnosticsGRPCPlugin{},
|
||||
"resource": &grpcplugin.ResourceGRPCPlugin{},
|
||||
"data": &grpcplugin.DataGRPCPlugin{},
|
||||
"stream": &grpcplugin.StreamGRPCPlugin{},
|
||||
"renderer": &pluginextensionv2.RendererGRPCPlugin{},
|
||||
"secretsmanager": &secretsmanagerplugin.SecretsManagerGRPCPlugin{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,3 +91,16 @@ func NewRendererPlugin(pluginID, executablePath string, startFn StartRendererFun
|
||||
startRendererFn: startFn,
|
||||
})
|
||||
}
|
||||
|
||||
// NewSecetsManagerPlugin creates a new secrets manager plugin factory used for registering a backend secrets manager plugin.
|
||||
func NewSecretsManagerPlugin(pluginID, executablePath string, startFn StartSecretsManagerFunc) backendplugin.PluginFactoryFunc {
|
||||
return newPlugin(PluginDescriptor{
|
||||
pluginID: pluginID,
|
||||
executablePath: executablePath,
|
||||
managed: false,
|
||||
versionedPlugins: map[int]goplugin.PluginSet{
|
||||
grpcplugin.ProtocolVersion: getV2PluginSet(),
|
||||
},
|
||||
startSecretsmanagerFn: startFn,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
|
||||
"github.com/grafana/grafana/pkg/util/errutil"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -24,6 +25,7 @@ type ClientV2 struct {
|
||||
grpcplugin.DataClient
|
||||
grpcplugin.StreamClient
|
||||
pluginextensionv2.RendererPlugin
|
||||
secretsmanagerplugin.SecretsManagerPlugin
|
||||
}
|
||||
|
||||
func newClientV2(descriptor PluginDescriptor, logger log.Logger, rpcClient plugin.ClientProtocol) (pluginClient, error) {
|
||||
@@ -52,6 +54,11 @@ func newClientV2(descriptor PluginDescriptor, logger log.Logger, rpcClient plugi
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawSecretsManager, err := rpcClient.Dispense("secretsmanager")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := ClientV2{}
|
||||
if rawDiagnostics != nil {
|
||||
if diagnosticsClient, ok := rawDiagnostics.(grpcplugin.DiagnosticsClient); ok {
|
||||
@@ -83,12 +90,24 @@ func newClientV2(descriptor PluginDescriptor, logger log.Logger, rpcClient plugi
|
||||
}
|
||||
}
|
||||
|
||||
if rawSecretsManager != nil {
|
||||
if secretsManagerPlugin, ok := rawSecretsManager.(secretsmanagerplugin.SecretsManagerPlugin); ok {
|
||||
c.SecretsManagerPlugin = secretsManagerPlugin
|
||||
}
|
||||
}
|
||||
|
||||
if descriptor.startRendererFn != nil {
|
||||
if err := descriptor.startRendererFn(descriptor.pluginID, c.RendererPlugin, logger); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if descriptor.startSecretsmanagerFn != nil {
|
||||
if err := descriptor.startSecretsmanagerFn(descriptor.pluginID, c.SecretsManagerPlugin, logger); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/grpcplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
|
||||
)
|
||||
|
||||
// PluginBackendProvider is a function type for initializing a Plugin backend.
|
||||
@@ -24,7 +25,7 @@ type Service struct {
|
||||
|
||||
func New(providers ...PluginBackendProvider) *Service {
|
||||
if len(providers) == 0 {
|
||||
return New(RendererProvider, DefaultProvider)
|
||||
return New(RendererProvider, SecretsManagerProvider, DefaultProvider)
|
||||
}
|
||||
return &Service{
|
||||
providerChain: providers,
|
||||
@@ -32,7 +33,7 @@ func New(providers ...PluginBackendProvider) *Service {
|
||||
}
|
||||
|
||||
func ProvideService(coreRegistry *coreplugin.Registry) *Service {
|
||||
return New(coreRegistry.BackendFactoryProvider(), RendererProvider, DefaultProvider)
|
||||
return New(coreRegistry.BackendFactoryProvider(), RendererProvider, SecretsManagerProvider, DefaultProvider)
|
||||
}
|
||||
|
||||
func (s *Service) BackendFactory(ctx context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||
@@ -56,6 +57,18 @@ var RendererProvider PluginBackendProvider = func(_ context.Context, p *plugins.
|
||||
)
|
||||
}
|
||||
|
||||
var SecretsManagerProvider PluginBackendProvider = func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||
if !p.IsSecretsManager() {
|
||||
return nil
|
||||
}
|
||||
return grpcplugin.NewSecretsManagerPlugin(p.ID, filepath.Join(p.PluginDir, secretsManagerStartCmd()),
|
||||
func(pluginID string, secretsmanager secretsmanagerplugin.SecretsManagerPlugin, logger log.Logger) error {
|
||||
p.SecretsManager = secretsmanager
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
var DefaultProvider PluginBackendProvider = func(_ context.Context, p *plugins.Plugin) backendplugin.PluginFactoryFunc {
|
||||
// TODO check for executable
|
||||
return grpcplugin.NewBackendPlugin(p.ID, filepath.Join(p.PluginDir, pluginStartCmd(p.Executable)))
|
||||
@@ -84,3 +97,15 @@ func rendererStartCmd() string {
|
||||
|
||||
return fmt.Sprintf("%s_%s_%s%s", "plugin_start", os, strings.ToLower(arch), extension)
|
||||
}
|
||||
|
||||
func secretsManagerStartCmd() string {
|
||||
os := strings.ToLower(runtime.GOOS)
|
||||
arch := runtime.GOARCH
|
||||
extension := ""
|
||||
|
||||
if os == "windows" {
|
||||
extension = ".exe"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s_%s_%s%s", "secrets_plugin_start", os, strings.ToLower(arch), extension)
|
||||
}
|
||||
|
||||
@@ -60,6 +60,11 @@ type RendererManager interface {
|
||||
Renderer() *Plugin
|
||||
}
|
||||
|
||||
type SecretsManagerManager interface {
|
||||
// SecretsManager returns a secretsmanager plugin
|
||||
SecretsManager() *Plugin
|
||||
}
|
||||
|
||||
type StaticRouteResolver interface {
|
||||
Routes() []*StaticRoute
|
||||
}
|
||||
|
||||
@@ -79,6 +79,36 @@ func TestInitializer_Initialize(t *testing.T) {
|
||||
assert.NotNil(t, c)
|
||||
})
|
||||
|
||||
t.Run("secretsmanager", func(t *testing.T) {
|
||||
p := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "test",
|
||||
Type: plugins.SecretsManager,
|
||||
Dependencies: plugins.Dependencies{
|
||||
GrafanaVersion: ">=8.x",
|
||||
},
|
||||
Backend: true,
|
||||
},
|
||||
PluginDir: absCurPath,
|
||||
Class: plugins.External,
|
||||
}
|
||||
|
||||
i := &Initializer{
|
||||
cfg: plugins.NewCfg(),
|
||||
log: log.NewNopLogger(),
|
||||
backendProvider: &fakeBackendProvider{
|
||||
plugin: p,
|
||||
},
|
||||
}
|
||||
|
||||
err := i.Initialize(context.Background(), p)
|
||||
assert.NoError(t, err)
|
||||
|
||||
c, exists := p.Client()
|
||||
assert.True(t, exists)
|
||||
assert.NotNil(t, c)
|
||||
})
|
||||
|
||||
t.Run("non backend plugin app", func(t *testing.T) {
|
||||
p := &plugins.Plugin{
|
||||
JSONData: plugins.JSONData{
|
||||
|
||||
@@ -23,6 +23,7 @@ var _ plugins.Client = (*PluginManager)(nil)
|
||||
var _ plugins.Store = (*PluginManager)(nil)
|
||||
var _ plugins.StaticRouteResolver = (*PluginManager)(nil)
|
||||
var _ plugins.RendererManager = (*PluginManager)(nil)
|
||||
var _ plugins.SecretsManagerManager = (*PluginManager)(nil)
|
||||
|
||||
type PluginManager struct {
|
||||
cfg *plugins.Cfg
|
||||
@@ -116,6 +117,16 @@ func (m *PluginManager) Renderer() *plugins.Plugin {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PluginManager) SecretsManager() *plugins.Plugin {
|
||||
for _, p := range m.plugins() {
|
||||
if p.IsSecretsManager() {
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *PluginManager) Routes() []*plugins.StaticRoute {
|
||||
staticRoutes := make([]*plugins.StaticRoute, 0)
|
||||
|
||||
|
||||
+17
-9
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/secretsmanagerplugin"
|
||||
)
|
||||
|
||||
type Plugin struct {
|
||||
@@ -36,9 +37,10 @@ type Plugin struct {
|
||||
Module string
|
||||
BaseURL string
|
||||
|
||||
Renderer pluginextensionv2.RendererPlugin
|
||||
client backendplugin.Plugin
|
||||
log log.Logger
|
||||
Renderer pluginextensionv2.RendererPlugin
|
||||
SecretsManager secretsmanagerplugin.SecretsManagerPlugin
|
||||
client backendplugin.Plugin
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
type PluginDTO struct {
|
||||
@@ -132,7 +134,7 @@ type JSONData struct {
|
||||
Streaming bool `json:"streaming"`
|
||||
SDK bool `json:"sdk,omitempty"`
|
||||
|
||||
// Backend (Datasource + Renderer)
|
||||
// Backend (Datasource + Renderer + SecretsManager)
|
||||
Executable string `json:"executable,omitempty"`
|
||||
}
|
||||
|
||||
@@ -347,6 +349,10 @@ func (p *Plugin) IsRenderer() bool {
|
||||
return p.Type == "renderer"
|
||||
}
|
||||
|
||||
func (p *Plugin) IsSecretsManager() bool {
|
||||
return p.Type == "secretsmanager"
|
||||
}
|
||||
|
||||
func (p *Plugin) IsDataSource() bool {
|
||||
return p.Type == "datasource"
|
||||
}
|
||||
@@ -384,20 +390,22 @@ var PluginTypes = []Type{
|
||||
Panel,
|
||||
App,
|
||||
Renderer,
|
||||
SecretsManager,
|
||||
}
|
||||
|
||||
type Type string
|
||||
|
||||
const (
|
||||
DataSource Type = "datasource"
|
||||
Panel Type = "panel"
|
||||
App Type = "app"
|
||||
Renderer Type = "renderer"
|
||||
DataSource Type = "datasource"
|
||||
Panel Type = "panel"
|
||||
App Type = "app"
|
||||
Renderer Type = "renderer"
|
||||
SecretsManager Type = "secretsmanager"
|
||||
)
|
||||
|
||||
func (pt Type) IsValid() bool {
|
||||
switch pt {
|
||||
case DataSource, Panel, App, Renderer:
|
||||
case DataSource, Panel, App, Renderer, SecretsManager:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user