Basic streaming plugin support (#31940)

This pull request migrates testdata to coreplugin streaming capabilities,
this is mostly a working concept of streaming plugins at the moment, 
the work will continue in the following pull requests.
This commit is contained in:
Alexander Emelin
2021-03-23 20:24:08 +03:00
committed by GitHub
parent 1cd8981be4
commit 336bc559a3
31 changed files with 1204 additions and 290 deletions
+122
View File
@@ -0,0 +1,122 @@
package features
import (
"context"
"fmt"
"github.com/centrifugal/centrifuge"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/models"
)
//go:generate mockgen -destination=mock.go -package=features github.com/grafana/grafana/pkg/services/live/features ChannelPublisher,PresenceGetter,PluginContextGetter,StreamRunner
type ChannelPublisher interface {
Publish(channel string, data []byte) error
}
type PresenceGetter interface {
GetNumSubscribers(channel string) (int, error)
}
type PluginContextGetter interface {
GetPluginContext(ctx context.Context, pluginID string, datasourceUID string) (backend.PluginContext, bool, error)
}
type StreamRunner interface {
RunStream(ctx context.Context, request *backend.RunStreamRequest, sender backend.StreamPacketSender) error
}
type streamSender struct {
channel string
channelPublisher ChannelPublisher
}
func newStreamSender(channel string, publisher ChannelPublisher) *streamSender {
return &streamSender{channel: channel, channelPublisher: publisher}
}
func (p *streamSender) Send(packet *backend.StreamPacket) error {
return p.channelPublisher.Publish(p.channel, packet.Payload)
}
// PluginRunner can handle streaming operations for channels belonging to plugins.
type PluginRunner struct {
pluginID string
datasourceUID string
pluginContextGetter PluginContextGetter
handler backend.StreamHandler
streamManager *StreamManager
}
// NewPluginRunner creates new PluginRunner.
func NewPluginRunner(pluginID string, datasourceUID string, streamManager *StreamManager, pluginContextGetter PluginContextGetter, handler backend.StreamHandler) *PluginRunner {
return &PluginRunner{
pluginID: pluginID,
datasourceUID: datasourceUID,
pluginContextGetter: pluginContextGetter,
handler: handler,
streamManager: streamManager,
}
}
// GetHandlerForPath gets the handler for a path.
func (m *PluginRunner) GetHandlerForPath(path string) (models.ChannelHandler, error) {
return &PluginPathRunner{
path: path,
pluginID: m.pluginID,
datasourceUID: m.datasourceUID,
streamManager: m.streamManager,
handler: m.handler,
pluginContextGetter: m.pluginContextGetter,
}, nil
}
// PluginPathRunner can handle streaming operations for channels belonging to plugin specific path.
type PluginPathRunner struct {
path string
pluginID string
datasourceUID string
streamManager *StreamManager
handler backend.StreamHandler
pluginContextGetter PluginContextGetter
}
// OnSubscribe passes control to a plugin.
func (r *PluginPathRunner) OnSubscribe(client *centrifuge.Client, e centrifuge.SubscribeEvent) (centrifuge.SubscribeReply, error) {
pCtx, found, err := r.pluginContextGetter.GetPluginContext(client.Context(), r.pluginID, r.datasourceUID)
if err != nil {
logger.Error("Get plugin context error", "error", err, "path", r.path)
return centrifuge.SubscribeReply{}, err
}
if !found {
logger.Error("Plugin context not found", "path", r.path)
return centrifuge.SubscribeReply{}, centrifuge.ErrorInternal
}
resp, err := r.handler.CanSubscribeToStream(client.Context(), &backend.SubscribeToStreamRequest{
PluginContext: pCtx,
Path: r.path,
})
if err != nil {
logger.Error("Plugin CanSubscribeToStream call error", "error", err, "path", r.path)
return centrifuge.SubscribeReply{}, err
}
if !resp.OK {
return centrifuge.SubscribeReply{}, centrifuge.ErrorPermissionDenied
}
err = r.streamManager.SubmitStream(e.Channel, r.path, pCtx, r.handler)
if err != nil {
logger.Error("Error submitting stream to manager", "error", err, "path", r.path)
return centrifuge.SubscribeReply{}, centrifuge.ErrorInternal
}
return centrifuge.SubscribeReply{
Options: centrifuge.SubscribeOptions{
Presence: true,
},
}, nil
}
// OnPublish passes control to a plugin.
func (r *PluginPathRunner) OnPublish(_ *centrifuge.Client, _ centrifuge.PublishEvent) (centrifuge.PublishReply, error) {
return centrifuge.PublishReply{}, fmt.Errorf("not implemented yet")
}