Backend plugins: Implement support for resources (#21805)
Implements initial support for resources using v0.14.0 of SDK. Ref #21512
This commit is contained in:
@@ -6,19 +6,24 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/collector"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
plugin "github.com/hashicorp/go-plugin"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrPluginNotRegistered error returned when plugin not registered.
|
||||
ErrPluginNotRegistered = errors.New("Plugin not registered")
|
||||
// ErrDiagnosticsNotSupported error returned when plugin doesn't support diagnostics.
|
||||
ErrDiagnosticsNotSupported = errors.New("Plugin diagnostics not supported")
|
||||
// ErrHealthCheckFailed error returned when health check failed.
|
||||
ErrHealthCheckFailed = errors.New("Health check failed")
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Register(®istry.Descriptor{
|
||||
Name: "BackendPluginManager",
|
||||
@@ -33,10 +38,13 @@ type Manager interface {
|
||||
Register(descriptor PluginDescriptor) error
|
||||
// StartPlugin starts a non-managed backend plugin
|
||||
StartPlugin(ctx context.Context, pluginID string) error
|
||||
// CheckHealth checks the health of a registered backend plugin.
|
||||
CheckHealth(ctx context.Context, pluginID string) (*CheckHealthResult, error)
|
||||
// CallResource calls a plugin resource.
|
||||
CallResource(ctx context.Context, req CallResourceRequest) (*CallResourceResult, error)
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
RouteRegister routing.RouteRegister `inject:""`
|
||||
pluginsMu sync.RWMutex
|
||||
plugins map[string]*BackendPlugin
|
||||
pluginCollector collector.PluginCollector
|
||||
@@ -49,8 +57,6 @@ func (m *manager) Init() error {
|
||||
m.pluginCollector = collector.NewPluginCollector()
|
||||
prometheus.MustRegister(m.pluginCollector)
|
||||
|
||||
m.RouteRegister.Get("/api/plugins/:pluginId/health", m.checkHealth)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -140,38 +146,50 @@ func (m *manager) stop() {
|
||||
}
|
||||
}
|
||||
|
||||
// checkHealth http handler for checking plugin health.
|
||||
func (m *manager) checkHealth(c *models.ReqContext) {
|
||||
pluginID := c.Params("pluginId")
|
||||
// CheckHealth checks the health of a registered backend plugin.
|
||||
func (m *manager) CheckHealth(ctx context.Context, pluginID string) (*CheckHealthResult, error) {
|
||||
m.pluginsMu.RLock()
|
||||
p, registered := m.plugins[pluginID]
|
||||
m.pluginsMu.RUnlock()
|
||||
|
||||
if !registered || !p.supportsDiagnostics() {
|
||||
c.JsonApiErr(404, "Plugin not found", nil)
|
||||
return
|
||||
if !registered {
|
||||
return nil, ErrPluginNotRegistered
|
||||
}
|
||||
|
||||
res, err := p.checkHealth(c.Req.Context())
|
||||
if !p.supportsDiagnostics() {
|
||||
return nil, ErrDiagnosticsNotSupported
|
||||
}
|
||||
|
||||
res, err := p.checkHealth(ctx)
|
||||
if err != nil {
|
||||
p.logger.Error("Failed to check plugin health", "error", err)
|
||||
c.JSON(503, map[string]interface{}{
|
||||
"status": pluginv2.CheckHealth_Response_ERROR.String(),
|
||||
})
|
||||
return
|
||||
return nil, ErrHealthCheckFailed
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"status": res.Status.String(),
|
||||
"info": res.Info,
|
||||
return checkHealthResultFromProto(res), nil
|
||||
}
|
||||
|
||||
// CallResource calls a plugin resource.
|
||||
func (m *manager) CallResource(ctx context.Context, req CallResourceRequest) (*CallResourceResult, error) {
|
||||
m.pluginsMu.RLock()
|
||||
p, registered := m.plugins[req.Config.PluginID]
|
||||
m.pluginsMu.RUnlock()
|
||||
|
||||
if !registered {
|
||||
return nil, ErrPluginNotRegistered
|
||||
}
|
||||
|
||||
if res.Status != pluginv2.CheckHealth_Response_OK {
|
||||
c.JSON(503, payload)
|
||||
return
|
||||
res, err := p.callResource(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.JSON(200, payload)
|
||||
// Make sure a content type always is returned in response
|
||||
if _, exists := res.Headers["Content-Type"]; !exists {
|
||||
res.Headers["Content-Type"] = []string{"application/json"}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func startPluginAndRestartKilledProcesses(ctx context.Context, p *BackendPlugin) error {
|
||||
|
||||
Reference in New Issue
Block a user