Backend plugins: Renderer v2 plugin (#23625)

grafana-plugin-model is legacy and is replaced by new backend 
plugins SDK and architecture. Renderer is not part of SDK and 
we want to keep it that way for now since it's highly unlikely there 
will be more than one kind of renderer plugin.
So this PR adds support for renderer plugin v2.
Also adds support sending a Device Scale Factor parameter to the 
plugin v2 remote rendering service and by that replaces #22474.
Adds support sending a Headers parameter to the plugin v2 and
remote rendering service which for now only include 
Accect-Language header (the user locale in browser when using 
Grafana), ref grafana/grafana-image-renderer#45.
Fixes health check json details response.
Adds image renderer plugin configuration settings in defaults.ini 
and sample.ini.

Co-Authored-By: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Marcus Efraimsson
2020-04-21 16:16:41 +02:00
committed by GitHub
parent 97bb3dcf2d
commit 871ad73414
20 changed files with 743 additions and 62 deletions

View File

@ -6,15 +6,15 @@ import (
"net/http"
"time"
"github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
datasourceV1 "github.com/grafana/grafana-plugin-model/go/datasource"
rendererV1 "github.com/grafana/grafana-plugin-model/go/renderer"
"github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
"github.com/grafana/grafana/pkg/util/errutil"
plugin "github.com/hashicorp/go-plugin"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// BackendPlugin a registered backend plugin.
@ -61,6 +61,11 @@ func (p *BackendPlugin) start(ctx context.Context) error {
return err
}
rawRenderer, err := rpcClient.Dispense("renderer")
if err != nil {
return err
}
if rawDiagnostics != nil {
if plugin, ok := rawDiagnostics.(DiagnosticsPlugin); ok {
p.diagnostics = plugin
@ -86,6 +91,12 @@ func (p *BackendPlugin) start(ctx context.Context) error {
client.TransformPlugin = plugin
}
}
if rawRenderer != nil {
if plugin, ok := rawRenderer.(pluginextensionv2.RendererPlugin); ok {
client.RendererPlugin = plugin
}
}
} else {
raw, err := rpcClient.Dispense(p.id)
if err != nil {

View File

@ -3,11 +3,11 @@ package backendplugin
import (
"os/exec"
"github.com/grafana/grafana-plugin-sdk-go/backend/grpcplugin"
"github.com/grafana/grafana/pkg/infra/log"
datasourceV1 "github.com/grafana/grafana-plugin-model/go/datasource"
rendererV1 "github.com/grafana/grafana-plugin-model/go/renderer"
"github.com/grafana/grafana-plugin-sdk-go/backend/grpcplugin"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
goplugin "github.com/hashicorp/go-plugin"
)
@ -64,6 +64,17 @@ type PluginDescriptor struct {
startFns PluginStartFuncs
}
// getV2PluginSet returns list of plugins supported on v2.
func getV2PluginSet() goplugin.PluginSet {
return goplugin.PluginSet{
"diagnostics": &grpcplugin.DiagnosticsGRPCPlugin{},
"resource": &grpcplugin.ResourceGRPCPlugin{},
"data": &grpcplugin.DataGRPCPlugin{},
"transform": &grpcplugin.TransformGRPCPlugin{},
"renderer": &pluginextensionv2.RendererGRPCPlugin{},
}
}
// NewBackendPluginDescriptor creates a new backend plugin descriptor
// used for registering a backend datasource plugin.
func NewBackendPluginDescriptor(pluginID, executablePath string, startFns PluginStartFuncs) PluginDescriptor {
@ -75,12 +86,7 @@ func NewBackendPluginDescriptor(pluginID, executablePath string, startFns Plugin
DefaultProtocolVersion: {
pluginID: &datasourceV1.DatasourcePluginImpl{},
},
grpcplugin.ProtocolVersion: {
"diagnostics": &grpcplugin.DiagnosticsGRPCPlugin{},
"resource": &grpcplugin.ResourceGRPCPlugin{},
"data": &grpcplugin.DataGRPCPlugin{},
"transform": &grpcplugin.TransformGRPCPlugin{},
},
grpcplugin.ProtocolVersion: getV2PluginSet(),
},
startFns: startFns,
}
@ -97,6 +103,7 @@ func NewRendererPluginDescriptor(pluginID, executablePath string, startFns Plugi
DefaultProtocolVersion: {
pluginID: &rendererV1.RendererPluginImpl{},
},
grpcplugin.ProtocolVersion: getV2PluginSet(),
},
startFns: startFns,
}
@ -129,4 +136,5 @@ type Client struct {
ResourcePlugin ResourcePlugin
DataPlugin DataPlugin
TransformPlugin TransformPlugin
RendererPlugin pluginextensionv2.RendererPlugin
}

View File

@ -0,0 +1,16 @@
#!/bin/bash
# To compile all protobuf files in this repository, run
# "make protobuf" at the top-level.
set -eu
DST_DIR=../genproto/pluginv2
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
cd "$DIR"
protoc -I ./ rendererv2.proto --go_out=plugins=grpc:./

View File

@ -0,0 +1,35 @@
package pluginextensionv2
import (
"context"
"github.com/hashicorp/go-plugin"
"google.golang.org/grpc"
)
type RendererPlugin interface {
RendererClient
}
type RendererGRPCPlugin struct {
plugin.NetRPCUnsupportedPlugin
}
func (p *RendererGRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
return nil
}
func (p *RendererGRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
return &RendererGRPCClient{NewRendererClient(c)}, nil
}
type RendererGRPCClient struct {
RendererClient
}
func (m *RendererGRPCClient) Render(ctx context.Context, req *RenderRequest, opts ...grpc.CallOption) (*RenderResponse, error) {
return m.RendererClient.Render(ctx, req)
}
var _ RendererClient = &RendererGRPCClient{}
var _ plugin.GRPCPlugin = &RendererGRPCPlugin{}

View File

@ -0,0 +1,333 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: rendererv2.proto
package pluginextensionv2
import (
context "context"
fmt "fmt"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
math "math"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type StringList struct {
Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *StringList) Reset() { *m = StringList{} }
func (m *StringList) String() string { return proto.CompactTextString(m) }
func (*StringList) ProtoMessage() {}
func (*StringList) Descriptor() ([]byte, []int) {
return fileDescriptor_412d7c60977d55a2, []int{0}
}
func (m *StringList) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StringList.Unmarshal(m, b)
}
func (m *StringList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_StringList.Marshal(b, m, deterministic)
}
func (m *StringList) XXX_Merge(src proto.Message) {
xxx_messageInfo_StringList.Merge(m, src)
}
func (m *StringList) XXX_Size() int {
return xxx_messageInfo_StringList.Size(m)
}
func (m *StringList) XXX_DiscardUnknown() {
xxx_messageInfo_StringList.DiscardUnknown(m)
}
var xxx_messageInfo_StringList proto.InternalMessageInfo
func (m *StringList) GetValues() []string {
if m != nil {
return m.Values
}
return nil
}
type RenderRequest struct {
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
Width int32 `protobuf:"varint,2,opt,name=width,proto3" json:"width,omitempty"`
Height int32 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"`
DeviceScaleFactor float32 `protobuf:"fixed32,4,opt,name=deviceScaleFactor,proto3" json:"deviceScaleFactor,omitempty"`
FilePath string `protobuf:"bytes,5,opt,name=filePath,proto3" json:"filePath,omitempty"`
RenderKey string `protobuf:"bytes,6,opt,name=renderKey,proto3" json:"renderKey,omitempty"`
Domain string `protobuf:"bytes,7,opt,name=domain,proto3" json:"domain,omitempty"`
Timeout int32 `protobuf:"varint,8,opt,name=timeout,proto3" json:"timeout,omitempty"`
Timezone string `protobuf:"bytes,9,opt,name=timezone,proto3" json:"timezone,omitempty"`
Headers map[string]*StringList `protobuf:"bytes,10,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RenderRequest) Reset() { *m = RenderRequest{} }
func (m *RenderRequest) String() string { return proto.CompactTextString(m) }
func (*RenderRequest) ProtoMessage() {}
func (*RenderRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_412d7c60977d55a2, []int{1}
}
func (m *RenderRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RenderRequest.Unmarshal(m, b)
}
func (m *RenderRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RenderRequest.Marshal(b, m, deterministic)
}
func (m *RenderRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RenderRequest.Merge(m, src)
}
func (m *RenderRequest) XXX_Size() int {
return xxx_messageInfo_RenderRequest.Size(m)
}
func (m *RenderRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RenderRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RenderRequest proto.InternalMessageInfo
func (m *RenderRequest) GetUrl() string {
if m != nil {
return m.Url
}
return ""
}
func (m *RenderRequest) GetWidth() int32 {
if m != nil {
return m.Width
}
return 0
}
func (m *RenderRequest) GetHeight() int32 {
if m != nil {
return m.Height
}
return 0
}
func (m *RenderRequest) GetDeviceScaleFactor() float32 {
if m != nil {
return m.DeviceScaleFactor
}
return 0
}
func (m *RenderRequest) GetFilePath() string {
if m != nil {
return m.FilePath
}
return ""
}
func (m *RenderRequest) GetRenderKey() string {
if m != nil {
return m.RenderKey
}
return ""
}
func (m *RenderRequest) GetDomain() string {
if m != nil {
return m.Domain
}
return ""
}
func (m *RenderRequest) GetTimeout() int32 {
if m != nil {
return m.Timeout
}
return 0
}
func (m *RenderRequest) GetTimezone() string {
if m != nil {
return m.Timezone
}
return ""
}
func (m *RenderRequest) GetHeaders() map[string]*StringList {
if m != nil {
return m.Headers
}
return nil
}
type RenderResponse struct {
Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RenderResponse) Reset() { *m = RenderResponse{} }
func (m *RenderResponse) String() string { return proto.CompactTextString(m) }
func (*RenderResponse) ProtoMessage() {}
func (*RenderResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_412d7c60977d55a2, []int{2}
}
func (m *RenderResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RenderResponse.Unmarshal(m, b)
}
func (m *RenderResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RenderResponse.Marshal(b, m, deterministic)
}
func (m *RenderResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RenderResponse.Merge(m, src)
}
func (m *RenderResponse) XXX_Size() int {
return xxx_messageInfo_RenderResponse.Size(m)
}
func (m *RenderResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RenderResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RenderResponse proto.InternalMessageInfo
func (m *RenderResponse) GetError() string {
if m != nil {
return m.Error
}
return ""
}
func init() {
proto.RegisterType((*StringList)(nil), "pluginextensionv2.StringList")
proto.RegisterType((*RenderRequest)(nil), "pluginextensionv2.RenderRequest")
proto.RegisterMapType((map[string]*StringList)(nil), "pluginextensionv2.RenderRequest.HeadersEntry")
proto.RegisterType((*RenderResponse)(nil), "pluginextensionv2.RenderResponse")
}
func init() {
proto.RegisterFile("rendererv2.proto", fileDescriptor_412d7c60977d55a2)
}
var fileDescriptor_412d7c60977d55a2 = []byte{
// 380 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x5f, 0x6f, 0xd3, 0x30,
0x14, 0xc5, 0x95, 0x86, 0xa4, 0xcd, 0x2d, 0xa0, 0xd6, 0xfc, 0x91, 0x55, 0x81, 0x14, 0x2a, 0x84,
0xf2, 0x00, 0x79, 0x48, 0x5f, 0x10, 0xbc, 0x21, 0xf1, 0x47, 0x02, 0x24, 0xe4, 0x3e, 0x95, 0xb7,
0xac, 0xb9, 0x6b, 0xac, 0xa6, 0x76, 0xe7, 0x38, 0xd9, 0xb2, 0x6f, 0xb4, 0x6f, 0x39, 0xc5, 0x4e,
0xd6, 0x4d, 0x9d, 0xb6, 0xb7, 0xfb, 0xf3, 0x3d, 0xbe, 0xc7, 0x3e, 0x36, 0x4c, 0x14, 0x8a, 0x0c,
0x15, 0xaa, 0x3a, 0x89, 0xf7, 0x4a, 0x6a, 0x49, 0xa6, 0xfb, 0xa2, 0xda, 0x70, 0x81, 0x17, 0x1a,
0x45, 0xc9, 0xa5, 0xa8, 0x93, 0xf9, 0x7b, 0x80, 0xa5, 0x56, 0x5c, 0x6c, 0xfe, 0xf0, 0x52, 0x93,
0xd7, 0xe0, 0xd7, 0x69, 0x51, 0x61, 0x49, 0x9d, 0xd0, 0x8d, 0x02, 0xd6, 0xd1, 0xfc, 0xca, 0x85,
0x67, 0xcc, 0x4c, 0x63, 0x78, 0x56, 0x61, 0xa9, 0xc9, 0x04, 0xdc, 0x4a, 0x15, 0xd4, 0x09, 0x9d,
0x28, 0x60, 0x6d, 0x49, 0x5e, 0x82, 0x77, 0xce, 0x33, 0x9d, 0xd3, 0x41, 0xe8, 0x44, 0x1e, 0xb3,
0xd0, 0x4e, 0xcc, 0x91, 0x6f, 0x72, 0x4d, 0x5d, 0xb3, 0xdc, 0x11, 0xf9, 0x08, 0xd3, 0x0c, 0x6b,
0xbe, 0xc6, 0xe5, 0x3a, 0x2d, 0xf0, 0x47, 0xba, 0xd6, 0x52, 0xd1, 0x27, 0xa1, 0x13, 0x0d, 0xd8,
0x71, 0x83, 0xcc, 0x60, 0x74, 0xca, 0x0b, 0xfc, 0x97, 0xea, 0x9c, 0x7a, 0xc6, 0xf2, 0x86, 0xc9,
0x1b, 0x08, 0xec, 0x45, 0x7f, 0x63, 0x43, 0x7d, 0xd3, 0x3c, 0x2c, 0xb4, 0xfe, 0x99, 0xdc, 0xa5,
0x5c, 0xd0, 0xa1, 0x69, 0x75, 0x44, 0x28, 0x0c, 0x35, 0xdf, 0xa1, 0xac, 0x34, 0x1d, 0x99, 0x83,
0xf5, 0xd8, 0x7a, 0xb5, 0xe5, 0xa5, 0x14, 0x48, 0x03, 0xeb, 0xd5, 0x33, 0xf9, 0x09, 0xc3, 0x1c,
0xd3, 0x0c, 0x55, 0x49, 0x21, 0x74, 0xa3, 0x71, 0xf2, 0x29, 0x3e, 0x8a, 0x34, 0xbe, 0x13, 0x54,
0xfc, 0xcb, 0xea, 0xbf, 0x0b, 0xad, 0x1a, 0xd6, 0xef, 0x9e, 0xad, 0xe0, 0xe9, 0xed, 0x46, 0x1b,
0xe7, 0x16, 0x9b, 0x3e, 0xce, 0x2d, 0x36, 0x64, 0x01, 0x9e, 0x09, 0xdf, 0xc4, 0x39, 0x4e, 0xde,
0xde, 0x63, 0x74, 0x78, 0x38, 0x66, 0xb5, 0x5f, 0x06, 0x9f, 0x9d, 0xf9, 0x07, 0x78, 0xde, 0x9f,
0xa0, 0xdc, 0x4b, 0x51, 0x62, 0xfb, 0x32, 0xa8, 0x94, 0x54, 0xdd, 0x78, 0x0b, 0xc9, 0x0a, 0x46,
0xac, 0xfb, 0x20, 0xe4, 0x2f, 0xf8, 0xb6, 0x26, 0xe1, 0x63, 0x17, 0x9a, 0xbd, 0x7b, 0x40, 0x61,
0x0d, 0xbf, 0xbd, 0xfa, 0xff, 0x22, 0xfe, 0x7a, 0xa4, 0x3a, 0xf1, 0xcd, 0x2f, 0x5c, 0x5c, 0x07,
0x00, 0x00, 0xff, 0xff, 0x36, 0x87, 0xfd, 0x2d, 0x99, 0x02, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConnInterface
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion6
// RendererClient is the client API for Renderer service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type RendererClient interface {
Render(ctx context.Context, in *RenderRequest, opts ...grpc.CallOption) (*RenderResponse, error)
}
type rendererClient struct {
cc grpc.ClientConnInterface
}
func NewRendererClient(cc grpc.ClientConnInterface) RendererClient {
return &rendererClient{cc}
}
func (c *rendererClient) Render(ctx context.Context, in *RenderRequest, opts ...grpc.CallOption) (*RenderResponse, error) {
out := new(RenderResponse)
err := c.cc.Invoke(ctx, "/pluginextensionv2.Renderer/Render", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// RendererServer is the server API for Renderer service.
type RendererServer interface {
Render(context.Context, *RenderRequest) (*RenderResponse, error)
}
// UnimplementedRendererServer can be embedded to have forward compatible implementations.
type UnimplementedRendererServer struct {
}
func (*UnimplementedRendererServer) Render(ctx context.Context, req *RenderRequest) (*RenderResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Render not implemented")
}
func RegisterRendererServer(s *grpc.Server, srv RendererServer) {
s.RegisterService(&_Renderer_serviceDesc, srv)
}
func _Renderer_Render_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RenderRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(RendererServer).Render(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pluginextensionv2.Renderer/Render",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(RendererServer).Render(ctx, req.(*RenderRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Renderer_serviceDesc = grpc.ServiceDesc{
ServiceName: "pluginextensionv2.Renderer",
HandlerType: (*RendererServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Render",
Handler: _Renderer_Render_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "rendererv2.proto",
}

View File

@ -0,0 +1,29 @@
syntax = "proto3";
package pluginextensionv2;
option go_package = ".;pluginextensionv2";
message StringList {
repeated string values = 1;
}
message RenderRequest {
string url = 1;
int32 width = 2;
int32 height = 3;
float deviceScaleFactor = 4;
string filePath = 5;
string renderKey = 6;
string domain = 7;
int32 timeout = 8;
string timezone = 9;
map<string, StringList> headers = 10;
}
message RenderResponse {
string error = 1;
}
service Renderer {
rpc Render(RenderRequest) returns (RenderResponse);
}