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
co-authored by Arve Knudsen
parent 97bb3dcf2d
commit 871ad73414
20 changed files with 743 additions and 62 deletions
+5
View File
@@ -46,6 +46,7 @@ func (rs *RenderingService) renderViaHttp(ctx context.Context, renderKey string,
queryParams.Add("timezone", isoTimeOffsetToPosixTz(opts.Timezone))
queryParams.Add("encoding", opts.Encoding)
queryParams.Add("timeout", strconv.Itoa(int(opts.Timeout.Seconds())))
queryParams.Add("deviceScaleFactor", fmt.Sprintf("%f", opts.DeviceScaleFactor))
rendererUrl.RawQuery = queryParams.Encode()
req, err := http.NewRequest("GET", rendererUrl.String(), nil)
@@ -55,6 +56,10 @@ func (rs *RenderingService) renderViaHttp(ctx context.Context, renderKey string,
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
for k, v := range opts.Headers {
req.Header[k] = v
}
// gives service some additional time to timeout and return possible errors.
reqContext, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
defer cancel()
+12 -10
View File
@@ -13,16 +13,18 @@ var ErrNoRenderer = errors.New("No renderer plugin found nor is an external rend
var ErrPhantomJSNotInstalled = errors.New("PhantomJS executable not found")
type Opts struct {
Width int
Height int
Timeout time.Duration
OrgId int64
UserId int64
OrgRole models.RoleType
Path string
Encoding string
Timezone string
ConcurrentLimit int
Width int
Height int
Timeout time.Duration
OrgId int64
UserId int64
OrgRole models.RoleType
Path string
Encoding string
Timezone string
ConcurrentLimit int
DeviceScaleFactor float64
Headers map[string][]string
}
type RenderResult struct {
+53 -5
View File
@@ -6,6 +6,7 @@ import (
"time"
pluginModel "github.com/grafana/grafana-plugin-model/go/renderer"
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
)
func (rs *RenderingService) startPlugin(ctx context.Context) error {
@@ -13,15 +14,23 @@ func (rs *RenderingService) startPlugin(ctx context.Context) error {
}
func (rs *RenderingService) renderViaPlugin(ctx context.Context, renderKey string, opts Opts) (*RenderResult, error) {
// gives plugin some additional time to timeout and return possible errors.
ctx, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
defer cancel()
if rs.pluginInfo.GrpcPluginV2 != nil {
return rs.renderViaPluginV2(ctx, renderKey, opts)
}
return rs.renderViaPluginV1(ctx, renderKey, opts)
}
func (rs *RenderingService) renderViaPluginV1(ctx context.Context, renderKey string, opts Opts) (*RenderResult, error) {
pngPath, err := rs.getFilePathForNewImage()
if err != nil {
return nil, err
}
// gives plugin some additional time to timeout and return possible errors.
ctx, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
defer cancel()
req := &pluginModel.RenderRequest{
Url: rs.getURL(opts.Path),
Width: int32(opts.Width),
@@ -35,7 +44,46 @@ func (rs *RenderingService) renderViaPlugin(ctx context.Context, renderKey strin
}
rs.log.Debug("calling renderer plugin", "req", req)
rsp, err := rs.pluginInfo.GrpcPlugin.Render(ctx, req)
rsp, err := rs.pluginInfo.GrpcPluginV1.Render(ctx, req)
if err != nil {
return nil, err
}
if rsp.Error != "" {
return nil, fmt.Errorf("rendering failed: %v", rsp.Error)
}
return &RenderResult{FilePath: pngPath}, nil
}
func (rs *RenderingService) renderViaPluginV2(ctx context.Context, renderKey string, opts Opts) (*RenderResult, error) {
pngPath, err := rs.getFilePathForNewImage()
if err != nil {
return nil, err
}
headers := map[string]*pluginextensionv2.StringList{}
for k, values := range opts.Headers {
headers[k] = &pluginextensionv2.StringList{
Values: values,
}
}
req := &pluginextensionv2.RenderRequest{
Url: rs.getURL(opts.Path),
Width: int32(opts.Width),
Height: int32(opts.Height),
DeviceScaleFactor: float32(opts.DeviceScaleFactor),
FilePath: pngPath,
Timeout: int32(opts.Timeout.Seconds()),
RenderKey: renderKey,
Timezone: isoTimeOffsetToPosixTz(opts.Timezone),
Domain: rs.domain,
Headers: headers,
}
rs.log.Debug("Calling renderer plugin", "req", req)
rsp, err := rs.pluginInfo.GrpcPluginV2.Render(ctx, req)
if err != nil {
return nil, err
}
+21 -16
View File
@@ -3,6 +3,7 @@ package rendering
import (
"context"
"fmt"
"math"
"net/url"
"os"
"path/filepath"
@@ -115,23 +116,27 @@ func (rs *RenderingService) Render(ctx context.Context, opts Opts) (*RenderResul
}, nil
}
if rs.renderAction != nil {
rs.log.Info("Rendering", "path", opts.Path)
renderKey, err := rs.generateAndStoreRenderKey(opts.OrgId, opts.UserId, opts.OrgRole)
if err != nil {
return nil, err
}
defer rs.deleteRenderKey(renderKey)
defer func() {
rs.inProgressCount--
}()
rs.inProgressCount++
return rs.renderAction(ctx, renderKey, opts)
if rs.renderAction == nil {
return nil, fmt.Errorf("no renderer found")
}
return nil, fmt.Errorf("No renderer found")
rs.log.Info("Rendering", "path", opts.Path)
if math.IsInf(opts.DeviceScaleFactor, 0) || math.IsNaN(opts.DeviceScaleFactor) || opts.DeviceScaleFactor <= 0 {
opts.DeviceScaleFactor = 1
}
renderKey, err := rs.generateAndStoreRenderKey(opts.OrgId, opts.UserId, opts.OrgRole)
if err != nil {
return nil, err
}
defer rs.deleteRenderKey(renderKey)
defer func() {
rs.inProgressCount--
}()
rs.inProgressCount++
return rs.renderAction(ctx, renderKey, opts)
}
func (rs *RenderingService) GetRenderUser(key string) (*RenderUser, bool) {