mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Rendering service - add optional RenderingSession (#44098)
* rendering service changes: - make node-renderer request timeout configurable - introduce optional RenderingSession providing a long-lived session key * remove console logs * added comment explaining empty "afterRequest" method * fix compilation error * update imports formatting * Update pkg/services/rendering/interface.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/services/rendering/rendering.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * review fix: extract renderKey related functions/structs to auth.go * #44449: private'd `rendering.getRequestTimeout` Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com>
This commit is contained in:
parent
3c334cd8ba
commit
5148250366
@ -53,12 +53,16 @@ func (hs *HTTPServer) RenderToPng(c *models.ReqContext) {
|
||||
}
|
||||
|
||||
result, err := hs.RenderService.Render(c.Req.Context(), rendering.Opts{
|
||||
TimeoutOpts: rendering.TimeoutOpts{
|
||||
Timeout: time.Duration(timeout) * time.Second,
|
||||
},
|
||||
AuthOpts: rendering.AuthOpts{
|
||||
OrgID: c.OrgId,
|
||||
UserID: c.UserId,
|
||||
OrgRole: c.OrgRole,
|
||||
},
|
||||
Width: width,
|
||||
Height: height,
|
||||
Timeout: time.Duration(timeout) * time.Second,
|
||||
OrgID: c.OrgId,
|
||||
UserID: c.UserId,
|
||||
OrgRole: c.OrgRole,
|
||||
Path: web.Params(c.Req)["*"] + queryParams,
|
||||
Timezone: queryReader.Get("tz", ""),
|
||||
Encoding: queryReader.Get("encoding", ""),
|
||||
@ -66,7 +70,7 @@ func (hs *HTTPServer) RenderToPng(c *models.ReqContext) {
|
||||
DeviceScaleFactor: scale,
|
||||
Headers: headers,
|
||||
Theme: rendering.ThemeDark,
|
||||
})
|
||||
}, nil)
|
||||
if err != nil {
|
||||
if errors.Is(err, rendering.ErrTimeout) {
|
||||
c.Handle(hs.Cfg, 500, err.Error(), err)
|
||||
|
@ -200,11 +200,15 @@ func (n *notificationService) renderAndUploadImage(evalCtx *EvalContext, timeout
|
||||
}
|
||||
|
||||
renderOpts := rendering.Opts{
|
||||
TimeoutOpts: rendering.TimeoutOpts{
|
||||
Timeout: timeout,
|
||||
},
|
||||
AuthOpts: rendering.AuthOpts{
|
||||
OrgID: evalCtx.Rule.OrgID,
|
||||
OrgRole: models.ROLE_ADMIN,
|
||||
},
|
||||
Width: 1000,
|
||||
Height: 500,
|
||||
Timeout: timeout,
|
||||
OrgID: evalCtx.Rule.OrgID,
|
||||
OrgRole: models.ROLE_ADMIN,
|
||||
ConcurrentLimit: setting.AlertingRenderLimit,
|
||||
Theme: rendering.ThemeDark,
|
||||
}
|
||||
@ -218,7 +222,7 @@ func (n *notificationService) renderAndUploadImage(evalCtx *EvalContext, timeout
|
||||
|
||||
n.log.Debug("Rendering alert panel image", "ruleId", evalCtx.Rule.ID, "urlPath", renderOpts.Path)
|
||||
start := time.Now()
|
||||
result, err := n.renderService.Render(evalCtx.Ctx, renderOpts)
|
||||
result, err := n.renderService.Render(evalCtx.Ctx, renderOpts, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ func (s *testRenderService) IsAvailable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *testRenderService) Render(ctx context.Context, opts rendering.Opts) (*rendering.RenderResult, error) {
|
||||
func (s *testRenderService) Render(ctx context.Context, opts rendering.Opts, session rendering.Session) (*rendering.RenderResult, error) {
|
||||
if s.renderProvider != nil {
|
||||
return s.renderProvider(ctx, opts)
|
||||
}
|
||||
@ -361,7 +361,7 @@ func (s *testRenderService) Render(ctx context.Context, opts rendering.Opts) (*r
|
||||
return &rendering.RenderResult{FilePath: "image.png"}, nil
|
||||
}
|
||||
|
||||
func (s *testRenderService) RenderCSV(ctx context.Context, opts rendering.CSVOpts) (*rendering.RenderCSVResult, error) {
|
||||
func (s *testRenderService) RenderCSV(ctx context.Context, opts rendering.CSVOpts, session rendering.Session) (*rendering.RenderCSVResult, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -381,6 +381,10 @@ func (s *testRenderService) Version() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *testRenderService) CreateRenderingSession(ctx context.Context, authOpts rendering.AuthOpts, sessionOpts rendering.SessionOpts) (rendering.Session, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var _ rendering.Service = &testRenderService{}
|
||||
|
||||
type testImageUploader struct {
|
||||
|
120
pkg/services/rendering/auth.go
Normal file
120
pkg/services/rendering/auth.go
Normal file
@ -0,0 +1,120 @@
|
||||
package rendering
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
const renderKeyPrefix = "render-%s"
|
||||
|
||||
type RenderUser struct {
|
||||
OrgID int64
|
||||
UserID int64
|
||||
OrgRole string
|
||||
}
|
||||
|
||||
func (rs *RenderingService) GetRenderUser(ctx context.Context, key string) (*RenderUser, bool) {
|
||||
val, err := rs.RemoteCacheService.Get(ctx, fmt.Sprintf(renderKeyPrefix, key))
|
||||
if err != nil {
|
||||
rs.log.Error("Failed to get render key from cache", "error", err)
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
if user, ok := val.(*RenderUser); ok {
|
||||
return user, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func setRenderKey(cache *remotecache.RemoteCache, ctx context.Context, opts AuthOpts, renderKey string, expiry time.Duration) error {
|
||||
err := cache.Set(ctx, fmt.Sprintf(renderKeyPrefix, renderKey), &RenderUser{
|
||||
OrgID: opts.OrgID,
|
||||
UserID: opts.UserID,
|
||||
OrgRole: string(opts.OrgRole),
|
||||
}, expiry)
|
||||
return err
|
||||
}
|
||||
|
||||
func generateAndSetRenderKey(cache *remotecache.RemoteCache, ctx context.Context, opts AuthOpts, expiry time.Duration) (string, error) {
|
||||
key, err := util.GetRandomString(32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = setRenderKey(cache, ctx, opts, key, expiry)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
type longLivedRenderKeyProvider struct {
|
||||
cache *remotecache.RemoteCache
|
||||
log log.Logger
|
||||
renderKey string
|
||||
authOpts AuthOpts
|
||||
sessionOpts SessionOpts
|
||||
}
|
||||
|
||||
func (rs *RenderingService) CreateRenderingSession(ctx context.Context, opts AuthOpts, sessionOpts SessionOpts) (Session, error) {
|
||||
renderKey, err := generateAndSetRenderKey(rs.RemoteCacheService, ctx, opts, sessionOpts.Expiry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &longLivedRenderKeyProvider{
|
||||
log: rs.log,
|
||||
renderKey: renderKey,
|
||||
cache: rs.RemoteCacheService,
|
||||
authOpts: opts,
|
||||
sessionOpts: sessionOpts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func deleteRenderKey(cache *remotecache.RemoteCache, log log.Logger, ctx context.Context, renderKey string) {
|
||||
err := cache.Delete(ctx, fmt.Sprintf(renderKeyPrefix, renderKey))
|
||||
if err != nil {
|
||||
log.Error("Failed to delete render key", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
type perRequestRenderKeyProvider struct {
|
||||
cache *remotecache.RemoteCache
|
||||
log log.Logger
|
||||
keyExpiry time.Duration
|
||||
}
|
||||
|
||||
func (r *perRequestRenderKeyProvider) get(ctx context.Context, opts AuthOpts) (string, error) {
|
||||
return generateAndSetRenderKey(r.cache, ctx, opts, r.keyExpiry)
|
||||
}
|
||||
|
||||
func (r *perRequestRenderKeyProvider) afterRequest(ctx context.Context, opts AuthOpts, renderKey string) {
|
||||
deleteRenderKey(r.cache, r.log, ctx, renderKey)
|
||||
}
|
||||
|
||||
func (r *longLivedRenderKeyProvider) get(ctx context.Context, opts AuthOpts) (string, error) {
|
||||
if r.sessionOpts.RefreshExpiryOnEachRequest {
|
||||
err := setRenderKey(r.cache, ctx, opts, r.renderKey, r.sessionOpts.Expiry)
|
||||
if err != nil {
|
||||
r.log.Error("Failed to refresh render key", "error", err, "renderKey", r.renderKey)
|
||||
}
|
||||
}
|
||||
return r.renderKey, nil
|
||||
}
|
||||
|
||||
func (r *longLivedRenderKeyProvider) afterRequest(ctx context.Context, opts AuthOpts, renderKey string) {
|
||||
// do nothing - renderKey from longLivedRenderKeyProvider is deleted only after session expires
|
||||
// or someone calls session.Dispose()
|
||||
}
|
||||
|
||||
func (r *longLivedRenderKeyProvider) Dispose(ctx context.Context) {
|
||||
deleteRenderKey(r.cache, r.log, ctx, r.renderKey)
|
||||
}
|
@ -58,7 +58,7 @@ func (rs *RenderingService) renderViaHTTP(ctx context.Context, renderKey string,
|
||||
rendererURL.RawQuery = queryParams.Encode()
|
||||
|
||||
// gives service some additional time to timeout and return possible errors.
|
||||
reqContext, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
|
||||
reqContext, cancel := context.WithTimeout(ctx, getRequestTimeout(opts.TimeoutOpts))
|
||||
defer cancel()
|
||||
|
||||
resp, err := rs.doRequest(reqContext, rendererURL, opts.Headers)
|
||||
@ -103,7 +103,7 @@ func (rs *RenderingService) renderCSVViaHTTP(ctx context.Context, renderKey stri
|
||||
rendererURL.RawQuery = queryParams.Encode()
|
||||
|
||||
// gives service some additional time to timeout and return possible errors.
|
||||
reqContext, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
|
||||
reqContext, cancel := context.WithTimeout(ctx, getRequestTimeout(opts.TimeoutOpts))
|
||||
defer cancel()
|
||||
|
||||
resp, err := rs.doRequest(reqContext, rendererURL, opts.Headers)
|
||||
|
@ -26,13 +26,30 @@ const (
|
||||
ThemeDark Theme = "dark"
|
||||
)
|
||||
|
||||
type TimeoutOpts struct {
|
||||
Timeout time.Duration // Timeout param passed to image-renderer service
|
||||
RequestTimeoutMultiplier time.Duration // RequestTimeoutMultiplier used for plugin/HTTP request context timeout
|
||||
}
|
||||
|
||||
type AuthOpts struct {
|
||||
OrgID int64
|
||||
UserID int64
|
||||
OrgRole models.RoleType
|
||||
}
|
||||
|
||||
func getRequestTimeout(opt TimeoutOpts) time.Duration {
|
||||
if opt.RequestTimeoutMultiplier == 0 {
|
||||
return opt.Timeout * 2 // default
|
||||
}
|
||||
|
||||
return opt.Timeout * opt.RequestTimeoutMultiplier
|
||||
}
|
||||
|
||||
type Opts struct {
|
||||
TimeoutOpts
|
||||
AuthOpts
|
||||
Width int
|
||||
Height int
|
||||
Timeout time.Duration
|
||||
OrgID int64
|
||||
UserID int64
|
||||
OrgRole models.RoleType
|
||||
Path string
|
||||
Encoding string
|
||||
Timezone string
|
||||
@ -43,10 +60,8 @@ type Opts struct {
|
||||
}
|
||||
|
||||
type CSVOpts struct {
|
||||
Timeout time.Duration
|
||||
OrgID int64
|
||||
UserID int64
|
||||
OrgRole models.RoleType
|
||||
TimeoutOpts
|
||||
AuthOpts
|
||||
Path string
|
||||
Encoding string
|
||||
Timezone string
|
||||
@ -66,11 +81,27 @@ type RenderCSVResult struct {
|
||||
type renderFunc func(ctx context.Context, renderKey string, options Opts) (*RenderResult, error)
|
||||
type renderCSVFunc func(ctx context.Context, renderKey string, options CSVOpts) (*RenderCSVResult, error)
|
||||
|
||||
type renderKeyProvider interface {
|
||||
get(ctx context.Context, opts AuthOpts) (string, error)
|
||||
afterRequest(ctx context.Context, opts AuthOpts, renderKey string)
|
||||
}
|
||||
|
||||
type SessionOpts struct {
|
||||
Expiry time.Duration
|
||||
RefreshExpiryOnEachRequest bool
|
||||
}
|
||||
|
||||
type Session interface {
|
||||
renderKeyProvider
|
||||
Dispose(ctx context.Context)
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
IsAvailable() bool
|
||||
Version() string
|
||||
Render(ctx context.Context, opts Opts) (*RenderResult, error)
|
||||
RenderCSV(ctx context.Context, opts CSVOpts) (*RenderCSVResult, error)
|
||||
Render(ctx context.Context, opts Opts, session Session) (*RenderResult, error)
|
||||
RenderCSV(ctx context.Context, opts CSVOpts, session Session) (*RenderCSVResult, error)
|
||||
RenderErrorImage(theme Theme, error error) (*RenderResult, error)
|
||||
GetRenderUser(ctx context.Context, key string) (*RenderUser, bool)
|
||||
CreateRenderingSession(ctx context.Context, authOpts AuthOpts, sessionOpts SessionOpts) (Session, error)
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2"
|
||||
)
|
||||
@ -15,7 +14,7 @@ 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)
|
||||
ctx, cancel := context.WithTimeout(ctx, getRequestTimeout(opts.TimeoutOpts))
|
||||
defer cancel()
|
||||
|
||||
filePath, err := rs.getNewFilePath(RenderPNG)
|
||||
@ -62,7 +61,7 @@ func (rs *RenderingService) renderViaPlugin(ctx context.Context, renderKey strin
|
||||
|
||||
func (rs *RenderingService) renderCSVViaPlugin(ctx context.Context, renderKey string, opts CSVOpts) (*RenderCSVResult, error) {
|
||||
// gives plugin some additional time to timeout and return possible errors.
|
||||
ctx, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
|
||||
ctx, cancel := context.WithTimeout(ctx, getRequestTimeout(opts.TimeoutOpts))
|
||||
defer cancel()
|
||||
|
||||
filePath, err := rs.getNewFilePath(RenderCSV)
|
||||
|
@ -17,7 +17,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@ -28,13 +27,6 @@ func init() {
|
||||
}
|
||||
|
||||
const ServiceName = "RenderingService"
|
||||
const renderKeyPrefix = "render-%s"
|
||||
|
||||
type RenderUser struct {
|
||||
OrgID int64
|
||||
UserID int64
|
||||
OrgRole string
|
||||
}
|
||||
|
||||
type RenderingService struct {
|
||||
log log.Logger
|
||||
@ -46,9 +38,10 @@ type RenderingService struct {
|
||||
version string
|
||||
versionMutex sync.RWMutex
|
||||
|
||||
Cfg *setting.Cfg
|
||||
RemoteCacheService *remotecache.RemoteCache
|
||||
RendererPluginManager plugins.RendererManager
|
||||
perRequestRenderKeyProvider renderKeyProvider
|
||||
Cfg *setting.Cfg
|
||||
RemoteCacheService *remotecache.RemoteCache
|
||||
RendererPluginManager plugins.RendererManager
|
||||
}
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, remoteCache *remotecache.RemoteCache, rm plugins.RendererManager) (*RenderingService, error) {
|
||||
@ -81,11 +74,17 @@ func ProvideService(cfg *setting.Cfg, remoteCache *remotecache.RemoteCache, rm p
|
||||
domain = "localhost"
|
||||
}
|
||||
|
||||
logger := log.New("rendering")
|
||||
s := &RenderingService{
|
||||
perRequestRenderKeyProvider: &perRequestRenderKeyProvider{
|
||||
cache: remoteCache,
|
||||
log: logger,
|
||||
keyExpiry: 5 * time.Minute,
|
||||
},
|
||||
Cfg: cfg,
|
||||
RemoteCacheService: remoteCache,
|
||||
RendererPluginManager: rm,
|
||||
log: log.New("rendering"),
|
||||
log: logger,
|
||||
domain: domain,
|
||||
}
|
||||
return s, nil
|
||||
@ -195,9 +194,14 @@ func (rs *RenderingService) renderUnavailableImage() *RenderResult {
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *RenderingService) Render(ctx context.Context, opts Opts) (*RenderResult, error) {
|
||||
func (rs *RenderingService) Render(ctx context.Context, opts Opts, session Session) (*RenderResult, error) {
|
||||
startTime := time.Now()
|
||||
result, err := rs.render(ctx, opts)
|
||||
|
||||
renderKeyProvider := rs.perRequestRenderKeyProvider
|
||||
if session != nil {
|
||||
renderKeyProvider = session
|
||||
}
|
||||
result, err := rs.render(ctx, opts, renderKeyProvider)
|
||||
|
||||
elapsedTime := time.Since(startTime).Milliseconds()
|
||||
saveMetrics(elapsedTime, err, RenderPNG)
|
||||
@ -205,7 +209,7 @@ func (rs *RenderingService) Render(ctx context.Context, opts Opts) (*RenderResul
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (rs *RenderingService) render(ctx context.Context, opts Opts) (*RenderResult, error) {
|
||||
func (rs *RenderingService) render(ctx context.Context, opts Opts, renderKeyProvider renderKeyProvider) (*RenderResult, error) {
|
||||
if int(atomic.LoadInt32(&rs.inProgressCount)) > opts.ConcurrentLimit {
|
||||
rs.log.Warn("Could not render image, hit the currency limit", "concurrencyLimit", opts.ConcurrentLimit, "path", opts.Path)
|
||||
|
||||
@ -230,12 +234,12 @@ func (rs *RenderingService) render(ctx context.Context, opts Opts) (*RenderResul
|
||||
if math.IsInf(opts.DeviceScaleFactor, 0) || math.IsNaN(opts.DeviceScaleFactor) || opts.DeviceScaleFactor == 0 {
|
||||
opts.DeviceScaleFactor = 1
|
||||
}
|
||||
renderKey, err := rs.generateAndStoreRenderKey(ctx, opts.OrgID, opts.UserID, opts.OrgRole)
|
||||
renderKey, err := renderKeyProvider.get(ctx, opts.AuthOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rs.deleteRenderKey(ctx, renderKey)
|
||||
defer renderKeyProvider.afterRequest(ctx, opts.AuthOpts, renderKey)
|
||||
|
||||
defer func() {
|
||||
metrics.MRenderingQueue.Set(float64(atomic.AddInt32(&rs.inProgressCount, -1)))
|
||||
@ -245,9 +249,14 @@ func (rs *RenderingService) render(ctx context.Context, opts Opts) (*RenderResul
|
||||
return rs.renderAction(ctx, renderKey, opts)
|
||||
}
|
||||
|
||||
func (rs *RenderingService) RenderCSV(ctx context.Context, opts CSVOpts) (*RenderCSVResult, error) {
|
||||
func (rs *RenderingService) RenderCSV(ctx context.Context, opts CSVOpts, session Session) (*RenderCSVResult, error) {
|
||||
startTime := time.Now()
|
||||
result, err := rs.renderCSV(ctx, opts)
|
||||
|
||||
renderKeyProvider := rs.perRequestRenderKeyProvider
|
||||
if session != nil {
|
||||
renderKeyProvider = session
|
||||
}
|
||||
result, err := rs.renderCSV(ctx, opts, renderKeyProvider)
|
||||
|
||||
elapsedTime := time.Since(startTime).Milliseconds()
|
||||
saveMetrics(elapsedTime, err, RenderCSV)
|
||||
@ -255,7 +264,7 @@ func (rs *RenderingService) RenderCSV(ctx context.Context, opts CSVOpts) (*Rende
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (rs *RenderingService) renderCSV(ctx context.Context, opts CSVOpts) (*RenderCSVResult, error) {
|
||||
func (rs *RenderingService) renderCSV(ctx context.Context, opts CSVOpts, renderKeyProvider renderKeyProvider) (*RenderCSVResult, error) {
|
||||
if int(atomic.LoadInt32(&rs.inProgressCount)) > opts.ConcurrentLimit {
|
||||
return nil, ErrConcurrentLimitReached
|
||||
}
|
||||
@ -265,12 +274,12 @@ func (rs *RenderingService) renderCSV(ctx context.Context, opts CSVOpts) (*Rende
|
||||
}
|
||||
|
||||
rs.log.Info("Rendering", "path", opts.Path)
|
||||
renderKey, err := rs.generateAndStoreRenderKey(ctx, opts.OrgID, opts.UserID, opts.OrgRole)
|
||||
renderKey, err := renderKeyProvider.get(ctx, opts.AuthOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer rs.deleteRenderKey(ctx, renderKey)
|
||||
defer renderKeyProvider.afterRequest(ctx, opts.AuthOpts, renderKey)
|
||||
|
||||
defer func() {
|
||||
metrics.MRenderingQueue.Set(float64(atomic.AddInt32(&rs.inProgressCount, -1)))
|
||||
@ -280,21 +289,6 @@ func (rs *RenderingService) renderCSV(ctx context.Context, opts CSVOpts) (*Rende
|
||||
return rs.renderCSVAction(ctx, renderKey, opts)
|
||||
}
|
||||
|
||||
func (rs *RenderingService) GetRenderUser(ctx context.Context, key string) (*RenderUser, bool) {
|
||||
val, err := rs.RemoteCacheService.Get(ctx, fmt.Sprintf(renderKeyPrefix, key))
|
||||
if err != nil {
|
||||
rs.log.Error("Failed to get render key from cache", "error", err)
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
if user, ok := val.(*RenderUser); ok {
|
||||
return user, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (rs *RenderingService) getNewFilePath(rt RenderType) (string, error) {
|
||||
rand, err := util.GetRandomString(20)
|
||||
if err != nil {
|
||||
@ -340,31 +334,6 @@ func (rs *RenderingService) getURL(path string) string {
|
||||
return fmt.Sprintf("%s://%s:%s%s/%s&render=1", protocol, rs.domain, rs.Cfg.HTTPPort, subPath, path)
|
||||
}
|
||||
|
||||
func (rs *RenderingService) generateAndStoreRenderKey(ctx context.Context, orgId, userId int64, orgRole models.RoleType) (string, error) {
|
||||
key, err := util.GetRandomString(32)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = rs.RemoteCacheService.Set(ctx, fmt.Sprintf(renderKeyPrefix, key), &RenderUser{
|
||||
OrgID: orgId,
|
||||
UserID: userId,
|
||||
OrgRole: string(orgRole),
|
||||
}, 5*time.Minute)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (rs *RenderingService) deleteRenderKey(ctx context.Context, key string) {
|
||||
err := rs.RemoteCacheService.Delete(ctx, fmt.Sprintf(renderKeyPrefix, key))
|
||||
if err != nil {
|
||||
rs.log.Error("Failed to delete render key", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func isoTimeOffsetToPosixTz(isoOffset string) string {
|
||||
// invert offset
|
||||
if strings.HasPrefix(isoOffset, "UTC+") {
|
||||
|
@ -138,7 +138,7 @@ func TestRenderLimitImage(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
opts := Opts{Theme: tc.theme, ConcurrentLimit: 1}
|
||||
result, err := rs.Render(context.Background(), opts)
|
||||
result, err := rs.Render(context.Background(), opts, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expected, result.FilePath)
|
||||
})
|
||||
|
@ -139,9 +139,15 @@ func (r *simpleCrawler) Start(c *models.ReqContext, mode CrawlerMode, theme rend
|
||||
|
||||
r.mode = mode
|
||||
r.opts = rendering.Opts{
|
||||
OrgID: c.OrgId,
|
||||
UserID: c.UserId,
|
||||
OrgRole: c.OrgRole,
|
||||
AuthOpts: rendering.AuthOpts{
|
||||
OrgID: c.OrgId,
|
||||
UserID: c.UserId,
|
||||
OrgRole: c.OrgRole,
|
||||
},
|
||||
TimeoutOpts: rendering.TimeoutOpts{
|
||||
Timeout: 10 * time.Second,
|
||||
RequestTimeoutMultiplier: 3,
|
||||
},
|
||||
Theme: theme,
|
||||
ConcurrentLimit: 10,
|
||||
}
|
||||
@ -204,14 +210,11 @@ func (r *simpleCrawler) walk() {
|
||||
Width: 320,
|
||||
Height: 240,
|
||||
Path: panelURL,
|
||||
OrgID: r.opts.OrgID,
|
||||
UserID: r.opts.UserID,
|
||||
ConcurrentLimit: r.opts.ConcurrentLimit,
|
||||
OrgRole: r.opts.OrgRole,
|
||||
AuthOpts: r.opts.AuthOpts,
|
||||
TimeoutOpts: r.opts.TimeoutOpts,
|
||||
Theme: r.opts.Theme,
|
||||
Timeout: 10 * time.Second,
|
||||
DeviceScaleFactor: -5, // negative numbers will render larger then scale down
|
||||
})
|
||||
}, nil)
|
||||
if err != nil {
|
||||
tlog.Warn("error getting image", "err", err)
|
||||
r.status.Errors++
|
||||
|
Loading…
Reference in New Issue
Block a user