From e7b800f35a2462752b1de28f1473a7b72ca7ab40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agn=C3=A8s=20Toulet?= <35176601+AgnesToulet@users.noreply.github.com> Date: Thu, 24 Apr 2025 15:31:19 +0200 Subject: [PATCH] Rendering: Add support for rate limiter (#103987) * Rendering: Add support for rate limiter * remove unnecessary golint exception * add lint exception back --- pkg/api/render.go | 5 +++++ pkg/services/rendering/http_mode.go | 6 ++++++ pkg/services/rendering/interface.go | 1 + 3 files changed, 12 insertions(+) diff --git a/pkg/api/render.go b/pkg/api/render.go index 582c6ec1e4b..5db0b88fa35 100644 --- a/pkg/api/render.go +++ b/pkg/api/render.go @@ -98,6 +98,11 @@ func (hs *HTTPServer) RenderHandler(c *contextmodel.ReqContext) { Theme: themeModel, }, nil) if err != nil { + if errors.Is(err, rendering.ErrTooManyRequests) { + c.JsonApiErr(http.StatusTooManyRequests, "Too many rendering requests", err) + return + } + if errors.Is(err, rendering.ErrTimeout) { c.Handle(hs.Cfg, http.StatusInternalServerError, err.Error(), err) return diff --git a/pkg/services/rendering/http_mode.go b/pkg/services/rendering/http_mode.go index 705f9672240..ea15eb389e5 100644 --- a/pkg/services/rendering/http_mode.go +++ b/pkg/services/rendering/http_mode.go @@ -31,6 +31,7 @@ var netClient = &http.Client{ } const authTokenHeader = "X-Auth-Token" //#nosec G101 -- This is a false positive +const rateLimiterHeader = "X-Tenant-ID" var ( remoteVersionFetchInterval time.Duration = time.Second * 15 @@ -160,6 +161,7 @@ func (rs *RenderingService) doRequest(ctx context.Context, u *url.URL, headers m } req.Header.Set(authTokenHeader, rs.Cfg.RendererAuthToken) + req.Header.Set(rateLimiterHeader, rs.domain) req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", rs.Cfg.BuildVersion)) for k, v := range headers { req.Header[k] = v @@ -180,6 +182,10 @@ func (rs *RenderingService) doRequest(ctx context.Context, u *url.URL, headers m return nil, fmt.Errorf("failed to send request to remote rendering service: %w", err) } + if resp.StatusCode == http.StatusTooManyRequests { + return nil, ErrTooManyRequests + } + return resp, nil } diff --git a/pkg/services/rendering/interface.go b/pkg/services/rendering/interface.go index 9b5b2f17cdc..a54d2b92560 100644 --- a/pkg/services/rendering/interface.go +++ b/pkg/services/rendering/interface.go @@ -14,6 +14,7 @@ var ErrTimeout = errors.New("timeout error - you can set timeout in seconds with var ErrConcurrentLimitReached = errors.New("rendering concurrent limit reached") var ErrRenderUnavailable = errors.New("rendering plugin not available") var ErrServerTimeout = errutil.NewBase(errutil.StatusUnknown, "rendering.serverTimeout", errutil.WithPublicMessage("error trying to connect to image-renderer service")) +var ErrTooManyRequests = errutil.NewBase(errutil.StatusTooManyRequests, "rendering.tooManyRequests", errutil.WithPublicMessage("trying to send too many requests to image-renderer service")) type RenderType string