diff --git a/pkg/services/rendering/interface.go b/pkg/services/rendering/interface.go index 0aa83dbb888..9aee779906e 100644 --- a/pkg/services/rendering/interface.go +++ b/pkg/services/rendering/interface.go @@ -41,6 +41,7 @@ func getRequestTimeout(opt TimeoutOpts) time.Duration { type Opts struct { TimeoutOpts AuthOpts + ErrorOpts Width int Height int Path string @@ -52,6 +53,15 @@ type Opts struct { Theme models.Theme } +type ErrorOpts struct { + // ErrorConcurrentLimitReached returns an ErrConcurrentLimitReached + // error instead of a rendering limit exceeded image. + ErrorConcurrentLimitReached bool + // ErrorRenderUnavailable returns an ErrRunderUnavailable error + // instead of a rendering unavailable image. + ErrorRenderUnavailable bool +} + type CSVOpts struct { TimeoutOpts AuthOpts diff --git a/pkg/services/rendering/rendering.go b/pkg/services/rendering/rendering.go index 2de807cb5a4..694d93890f6 100644 --- a/pkg/services/rendering/rendering.go +++ b/pkg/services/rendering/rendering.go @@ -235,6 +235,9 @@ func (rs *RenderingService) Render(ctx context.Context, opts Opts, session Sessi 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) + if opts.ErrorConcurrentLimitReached { + return nil, ErrConcurrentLimitReached + } theme := models.ThemeDark if opts.Theme != "" { @@ -250,6 +253,9 @@ func (rs *RenderingService) render(ctx context.Context, opts Opts, renderKeyProv rs.log.Warn("Could not render image, no image renderer found/installed. " + "For image rendering support please install the grafana-image-renderer plugin. " + "Read more at https://grafana.com/docs/grafana/latest/administration/image_rendering/") + if opts.ErrorRenderUnavailable { + return nil, ErrRenderUnavailable + } return rs.renderUnavailableImage(), nil } diff --git a/pkg/services/rendering/rendering_test.go b/pkg/services/rendering/rendering_test.go index 5deb2e7805f..5f31a016d3e 100644 --- a/pkg/services/rendering/rendering_test.go +++ b/pkg/services/rendering/rendering_test.go @@ -11,6 +11,7 @@ import ( "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" + "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -102,6 +103,22 @@ func TestRenderErrorImage(t *testing.T) { }) } +type unavailableRendererManager struct{} + +func (m unavailableRendererManager) Renderer() *plugins.Plugin { return nil } + +func TestRenderUnavailableError(t *testing.T) { + rs := RenderingService{ + Cfg: &setting.Cfg{}, + log: log.New("test"), + RendererPluginManager: unavailableRendererManager{}, + } + opts := Opts{ErrorOpts: ErrorOpts{ErrorRenderUnavailable: true}} + result, err := rs.Render(context.Background(), opts, nil) + assert.Equal(t, ErrRenderUnavailable, err) + assert.Nil(t, result) +} + func TestRenderLimitImage(t *testing.T) { path, err := filepath.Abs("../../../") require.NoError(t, err) @@ -146,6 +163,22 @@ func TestRenderLimitImage(t *testing.T) { } } +func TestRenderLimitImageError(t *testing.T) { + rs := RenderingService{ + Cfg: &setting.Cfg{}, + inProgressCount: 2, + log: log.New("test"), + } + opts := Opts{ + ErrorOpts: ErrorOpts{ErrorConcurrentLimitReached: true}, + ConcurrentLimit: 1, + Theme: models.ThemeDark, + } + result, err := rs.Render(context.Background(), opts, nil) + assert.Equal(t, ErrConcurrentLimitReached, err) + assert.Nil(t, result) +} + func TestRenderingServiceGetRemotePluginVersion(t *testing.T) { cfg := setting.NewCfg() rs := &RenderingService{