mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
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:
parent
97bb3dcf2d
commit
871ad73414
12
Makefile
12
Makefile
@ -4,7 +4,7 @@
|
||||
|
||||
-include local/Makefile
|
||||
|
||||
.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-dev build-docker-full lint-go gosec revive golangci-lint go-vet test-go test-js test run run-frontend clean devenv devenv-down revive-alerting help
|
||||
.PHONY: all deps-go deps-js deps build-go build-server build-cli build-js build build-docker-dev build-docker-full lint-go gosec revive golangci-lint go-vet test-go test-js test run run-frontend clean devenv devenv-down revive-alerting protobuf help
|
||||
|
||||
GO = GO111MODULE=on go
|
||||
GO_FILES ?= ./pkg/...
|
||||
@ -161,6 +161,16 @@ devenv-down: ## Stop optional services.
|
||||
|
||||
##@ Helpers
|
||||
|
||||
# We separate the protobuf generation because most development tasks on
|
||||
# Grafana do not involve changing protobuf files and protoc is not a
|
||||
# go-gettable dependency and so getting it installed can be inconvenient.
|
||||
#
|
||||
# If you are working on changes to protobuf interfaces you may either use
|
||||
# this target or run the individual scripts below directly.
|
||||
protobuf: ## Compile protobuf definitions
|
||||
bash scripts/protobuf-check.sh
|
||||
bash pkg/plugins/backendplugin/pluginextensionv2/generate.sh
|
||||
|
||||
clean: ## Clean up intermediate build artifacts.
|
||||
@echo "cleaning"
|
||||
rm -rf node_modules
|
||||
|
@ -693,6 +693,66 @@ disable_sanitize_html = false
|
||||
enable_alpha = false
|
||||
app_tls_skip_verify_insecure = false
|
||||
|
||||
#################################### Grafana Image Renderer Plugin ##########################
|
||||
[plugin.grafana-image-renderer]
|
||||
# Instruct headless browser instance to use a default timezone when not provided by Grafana, e.g. when rendering panel image of alert.
|
||||
# See ICU’s metaZones.txt (https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt) for a list of supported
|
||||
# timezone IDs. Fallbacks to TZ environment variable if not set.
|
||||
rendering_timezone =
|
||||
|
||||
# Instruct headless browser instance to use a default language when not provided by Grafana, e.g. when rendering panel image of alert.
|
||||
# Please refer to the HTTP header Accept-Language to understand how to format this value, e.g. 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5'.
|
||||
rendering_language =
|
||||
|
||||
# Instruct headless browser instance to use a default device scale factor when not provided by Grafana, e.g. when rendering panel image of alert.
|
||||
# Default is 1. Using a higher value will produce more detailed images (higher DPI), but will require more disk space to store an image.
|
||||
rendering_viewport_device_scale_factor =
|
||||
|
||||
# Instruct headless browser instance whether to ignore HTTPS errors during navigation. Per default HTTPS errors are not ignored. Due to
|
||||
# the security risk it's not recommended to ignore HTTPS errors.
|
||||
rendering_ignore_https_errors =
|
||||
|
||||
# Instruct headless browser instance whether to capture and log verbose information when rendering an image. Default is false and will
|
||||
# only capture and log error messages. When enabled, debug messages are captured and logged as well.
|
||||
# For the verbose information to be included in the Grafana server log you have to adjust the rendering log level to debug, configure
|
||||
# [log].filter = rendering:debug.
|
||||
rendering_verbose_logging =
|
||||
|
||||
# Instruct headless browser instance whether to output its debug and error messages into running process of remote rendering service.
|
||||
# Default is false. This can be useful to enable (true) when troubleshooting.
|
||||
rendering_dumpio =
|
||||
|
||||
# Additional arguments to pass to the headless browser instance. Default is --no-sandbox. The list of Chromium flags can be found
|
||||
# here (https://peter.sh/experiments/chromium-command-line-switches/). Multiple arguments is separated with comma-character.
|
||||
rendering_args =
|
||||
|
||||
# You can configure the plugin to use a different browser binary instead of the pre-packaged version of Chromium.
|
||||
# Please note that this is not recommended, since you may encounter problems if the installed version of Chrome/Chromium is not
|
||||
# compatible with the plugin.
|
||||
rendering_chrome_bin =
|
||||
|
||||
# Instruct how headless browser instances are created. Default is 'default' and will create a new browser instance on each request.
|
||||
# Mode 'clustered' will make sure that only a maximum of browsers/incognito pages can execute concurrently.
|
||||
# Mode 'reusable' will have one browser instance and will create a new incognito page on each request.
|
||||
rendering_mode =
|
||||
|
||||
# When rendering_mode = clustered you can instruct how many browsers or incognito pages can execute concurrently. Default is 'browser'
|
||||
# and will cluster using browser instances.
|
||||
# Mode 'context' will cluster using incognito pages.
|
||||
rendering_clustering_mode =
|
||||
# When rendering_mode = clustered you can define maximum number of browser instances/incognito pages that can execute concurrently..
|
||||
rendering_clustering_max_concurrency =
|
||||
|
||||
# Limit the maxiumum viewport width, height and device scale factor that can be requested.
|
||||
rendering_viewport_max_width =
|
||||
rendering_viewport_max_height =
|
||||
rendering_viewport_max_device_scale_factor =
|
||||
|
||||
# Change the listening host and port of the gRPC server. Default host is 127.0.0.1 and default port is 0 and will automatically assign
|
||||
# a port not in use.
|
||||
grpc_host =
|
||||
grpc_port =
|
||||
|
||||
[enterprise]
|
||||
license_path =
|
||||
|
||||
|
@ -682,6 +682,66 @@
|
||||
;enable_alpha = false
|
||||
;app_tls_skip_verify_insecure = false
|
||||
|
||||
#################################### Grafana Image Renderer Plugin ##########################
|
||||
[plugin.grafana-image-renderer]
|
||||
# Instruct headless browser instance to use a default timezone when not provided by Grafana, e.g. when rendering panel image of alert.
|
||||
# See ICU’s metaZones.txt (https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt) for a list of supported
|
||||
# timezone IDs. Fallbacks to TZ environment variable if not set.
|
||||
;rendering_timezone =
|
||||
|
||||
# Instruct headless browser instance to use a default language when not provided by Grafana, e.g. when rendering panel image of alert.
|
||||
# Please refer to the HTTP header Accept-Language to understand how to format this value, e.g. 'fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5'.
|
||||
;rendering_language =
|
||||
|
||||
# Instruct headless browser instance to use a default device scale factor when not provided by Grafana, e.g. when rendering panel image of alert.
|
||||
# Default is 1. Using a higher value will produce more detailed images (higher DPI), but will require more disk space to store an image.
|
||||
;rendering_viewport_device_scale_factor =
|
||||
|
||||
# Instruct headless browser instance whether to ignore HTTPS errors during navigation. Per default HTTPS errors are not ignored. Due to
|
||||
# the security risk it's not recommended to ignore HTTPS errors.
|
||||
;rendering_ignore_https_errors =
|
||||
|
||||
# Instruct headless browser instance whether to capture and log verbose information when rendering an image. Default is false and will
|
||||
# only capture and log error messages. When enabled, debug messages are captured and logged as well.
|
||||
# For the verbose information to be included in the Grafana server log you have to adjust the rendering log level to debug, configure
|
||||
# [log].filter = rendering:debug.
|
||||
;rendering_verbose_logging =
|
||||
|
||||
# Instruct headless browser instance whether to output its debug and error messages into running process of remote rendering service.
|
||||
# Default is false. This can be useful to enable (true) when troubleshooting.
|
||||
;rendering_dumpio =
|
||||
|
||||
# Additional arguments to pass to the headless browser instance. Default is --no-sandbox. The list of Chromium flags can be found
|
||||
# here (https://peter.sh/experiments/chromium-command-line-switches/). Multiple arguments is separated with comma-character.
|
||||
;rendering_args =
|
||||
|
||||
# You can configure the plugin to use a different browser binary instead of the pre-packaged version of Chromium.
|
||||
# Please note that this is not recommended, since you may encounter problems if the installed version of Chrome/Chromium is not
|
||||
# compatible with the plugin.
|
||||
;rendering_chrome_bin =
|
||||
|
||||
# Instruct how headless browser instances are created. Default is 'default' and will create a new browser instance on each request.
|
||||
# Mode 'clustered' will make sure that only a maximum of browsers/incognito pages can execute concurrently.
|
||||
# Mode 'reusable' will have one browser instance and will create a new incognito page on each request.
|
||||
;rendering_mode =
|
||||
|
||||
# When rendering_mode = clustered you can instruct how many browsers or incognito pages can execute concurrently. Default is 'browser'
|
||||
# and will cluster using browser instances.
|
||||
# Mode 'context' will cluster using incognito pages.
|
||||
;rendering_clustering_mode =
|
||||
# When rendering_mode = clustered you can define maximum number of browser instances/incognito pages that can execute concurrently..
|
||||
;rendering_clustering_max_concurrency =
|
||||
|
||||
# Limit the maxiumum viewport width, height and device scale factor that can be requested.
|
||||
;rendering_viewport_max_width =
|
||||
;rendering_viewport_max_height =
|
||||
;rendering_viewport_max_device_scale_factor =
|
||||
|
||||
# Change the listening host and port of the gRPC server. Default host is 127.0.0.1 and default port is 0 and will automatically assign
|
||||
# a port not in use.
|
||||
;grpc_host =
|
||||
;grpc_port =
|
||||
|
||||
[enterprise]
|
||||
# Path to a valid Grafana Enterprise license.jwt file
|
||||
;license_path =
|
||||
|
1
go.mod
1
go.mod
@ -26,6 +26,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.4.1
|
||||
github.com/go-stack/stack v1.8.0
|
||||
github.com/gobwas/glob v0.2.3
|
||||
github.com/golang/protobuf v1.3.4
|
||||
github.com/google/go-cmp v0.3.1
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/gosimple/slug v1.4.2
|
||||
|
@ -386,15 +386,14 @@ func (hs *HTTPServer) CheckDatasourceHealth(c *models.ReqContext) {
|
||||
return
|
||||
}
|
||||
|
||||
var jsonDetails map[string]interface{}
|
||||
payload := map[string]interface{}{
|
||||
"status": resp.Status.String(),
|
||||
"message": resp.Message,
|
||||
"details": jsonDetails,
|
||||
}
|
||||
|
||||
// Unmarshal JSONDetails if it's not empty.
|
||||
if len(resp.JSONDetails) > 0 {
|
||||
var jsonDetails map[string]interface{}
|
||||
err = json.Unmarshal(resp.JSONDetails, &jsonDetails)
|
||||
if err != nil {
|
||||
c.JsonApiErr(500, "Failed to unmarshal detailed response from backend plugin", err)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"sort"
|
||||
@ -317,9 +318,19 @@ func (hs *HTTPServer) CheckHealth(c *models.ReqContext) Response {
|
||||
}
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"status": resp.Status.String(),
|
||||
"message": resp.Message,
|
||||
"jsonDetails": resp.JSONDetails,
|
||||
"status": resp.Status.String(),
|
||||
"message": resp.Message,
|
||||
}
|
||||
|
||||
// Unmarshal JSONDetails if it's not empty.
|
||||
if len(resp.JSONDetails) > 0 {
|
||||
var jsonDetails map[string]interface{}
|
||||
err = json.Unmarshal(resp.JSONDetails, &jsonDetails)
|
||||
if err != nil {
|
||||
return Error(500, "Failed to unmarshal detailed response from backend plugin", err)
|
||||
}
|
||||
|
||||
payload["details"] = jsonDetails
|
||||
}
|
||||
|
||||
if resp.Status != backendplugin.HealthStatusOk {
|
||||
|
@ -40,18 +40,32 @@ func (hs *HTTPServer) RenderToPng(c *models.ReqContext) {
|
||||
return
|
||||
}
|
||||
|
||||
scale, err := strconv.ParseFloat(queryReader.Get("scale", "1"), 64)
|
||||
if err != nil {
|
||||
c.Handle(400, "Render parameters error", fmt.Errorf("Cannot parse scale as float: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
headers := http.Header{}
|
||||
acceptLanguageHeader := c.Req.Header.Values("Accept-Language")
|
||||
if len(acceptLanguageHeader) > 0 {
|
||||
headers["Accept-Language"] = acceptLanguageHeader
|
||||
}
|
||||
|
||||
maxConcurrentLimitForApiCalls := 30
|
||||
result, err := hs.RenderService.Render(c.Req.Context(), rendering.Opts{
|
||||
Width: width,
|
||||
Height: height,
|
||||
Timeout: time.Duration(timeout) * time.Second,
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
OrgRole: c.OrgRole,
|
||||
Path: c.Params("*") + queryParams,
|
||||
Timezone: queryReader.Get("tz", ""),
|
||||
Encoding: queryReader.Get("encoding", ""),
|
||||
ConcurrentLimit: maxConcurrentLimitForApiCalls,
|
||||
Width: width,
|
||||
Height: height,
|
||||
Timeout: time.Duration(timeout) * time.Second,
|
||||
OrgId: c.OrgId,
|
||||
UserId: c.UserId,
|
||||
OrgRole: c.OrgRole,
|
||||
Path: c.Params("*") + queryParams,
|
||||
Timezone: queryReader.Get("tz", ""),
|
||||
Encoding: queryReader.Get("encoding", ""),
|
||||
ConcurrentLimit: maxConcurrentLimitForApiCalls,
|
||||
DeviceScaleFactor: scale,
|
||||
Headers: headers,
|
||||
})
|
||||
|
||||
if err != nil && err == rendering.ErrTimeout {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
16
pkg/plugins/backendplugin/pluginextensionv2/generate.sh
Executable file
16
pkg/plugins/backendplugin/pluginextensionv2/generate.sh
Executable 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:./
|
@ -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{}
|
333
pkg/plugins/backendplugin/pluginextensionv2/rendererv2.pb.go
Normal file
333
pkg/plugins/backendplugin/pluginextensionv2/rendererv2.pb.go
Normal 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",
|
||||
}
|
29
pkg/plugins/backendplugin/pluginextensionv2/rendererv2.proto
Normal file
29
pkg/plugins/backendplugin/pluginextensionv2/rendererv2.proto
Normal 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);
|
||||
}
|
@ -8,6 +8,7 @@ import (
|
||||
pluginModel "github.com/grafana/grafana-plugin-model/go/renderer"
|
||||
"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/util/errutil"
|
||||
)
|
||||
|
||||
@ -15,7 +16,8 @@ type RendererPlugin struct {
|
||||
PluginBase
|
||||
|
||||
Executable string `json:"executable,omitempty"`
|
||||
GrpcPlugin pluginModel.RendererPlugin
|
||||
GrpcPluginV1 pluginModel.RendererPlugin
|
||||
GrpcPluginV2 pluginextensionv2.RendererPlugin
|
||||
backendPluginManager backendplugin.Manager
|
||||
}
|
||||
|
||||
@ -34,6 +36,7 @@ func (r *RendererPlugin) Load(decoder *json.Decoder, pluginDir string, backendPl
|
||||
fullpath := path.Join(r.PluginDir, cmd)
|
||||
descriptor := backendplugin.NewRendererPluginDescriptor(r.Id, fullpath, backendplugin.PluginStartFuncs{
|
||||
OnLegacyStart: r.onLegacyPluginStart,
|
||||
OnStart: r.onPluginStart,
|
||||
})
|
||||
if err := backendPluginManager.Register(descriptor); err != nil {
|
||||
return errutil.Wrapf(err, "Failed to register backend plugin")
|
||||
@ -52,6 +55,11 @@ func (r *RendererPlugin) Start(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (r *RendererPlugin) onLegacyPluginStart(pluginID string, client *backendplugin.LegacyClient, logger log.Logger) error {
|
||||
r.GrpcPlugin = client.RendererPlugin
|
||||
r.GrpcPluginV1 = client.RendererPlugin
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RendererPlugin) onPluginStart(pluginID string, client *backendplugin.Client, logger log.Logger) error {
|
||||
r.GrpcPluginV2 = client.RendererPlugin
|
||||
return nil
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
25
scripts/protobuf-check.sh
Executable file
25
scripts/protobuf-check.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Check whether protobuf & go plugin are installed
|
||||
PROTOC_HELP_URL="http://google.github.io/proto-lens/installing-protoc.html"
|
||||
PROTOC_GEN_GO_HELP_URL="https://github.com/golang/protobuf/tree/v1.3.4#installation"
|
||||
|
||||
EXIT_CODE=0
|
||||
|
||||
if ! [ -x "$(command -v protoc)" ]; then
|
||||
echo "Protocol Buffers not found."
|
||||
echo "Please install Protocol Buffers and ensure 'protoc' is available in your PATH."
|
||||
echo "See ${PROTOC_HELP_URL} for more."
|
||||
echo
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
if ! [ -x "$(command -v protoc-gen-go)" ]; then
|
||||
echo "Protocol Buffers Go plugin not found."
|
||||
echo "Please install the plugin and ensure 'protoc-gen-go' is available in your PATH."
|
||||
echo "See ${PROTOC_GEN_GO_HELP_URL} for more."
|
||||
echo
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -144,6 +144,7 @@ github.com/gobwas/glob/syntax/lexer
|
||||
github.com/gobwas/glob/util/runes
|
||||
github.com/gobwas/glob/util/strings
|
||||
# github.com/golang/protobuf v1.3.4
|
||||
## explicit
|
||||
github.com/golang/protobuf/proto
|
||||
github.com/golang/protobuf/protoc-gen-go/descriptor
|
||||
github.com/golang/protobuf/ptypes
|
||||
|
Loading…
Reference in New Issue
Block a user