The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/pkg/services/rendering/http_mode.go

254 lines
6.7 KiB

package rendering
import (
"context"
"encoding/json"
"errors"
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
"fmt"
"io"
"io/fs"
"mime"
"net"
"net/http"
"net/url"
"os"
"strconv"
"time"
)
var netTransport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
}).Dial,
TLSHandshakeTimeout: 5 * time.Second,
}
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
var netClient = &http.Client{
Transport: netTransport,
}
var (
remoteVersionFetchInterval time.Duration = time.Second * 15
remoteVersionFetchRetries uint = 4
)
func (rs *RenderingService) renderViaHTTP(ctx context.Context, renderKey string, opts Opts) (*RenderResult, error) {
filePath, err := rs.getNewFilePath(RenderPNG)
if err != nil {
return nil, err
}
rendererURL, err := url.Parse(rs.Cfg.RendererUrl)
if err != nil {
return nil, err
}
queryParams := rendererURL.Query()
queryParams.Add("url", rs.getURL(opts.Path))
queryParams.Add("renderKey", renderKey)
queryParams.Add("width", strconv.Itoa(opts.Width))
queryParams.Add("height", strconv.Itoa(opts.Height))
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
queryParams.Add("domain", rs.domain)
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()
// gives service some additional time to timeout and return possible errors.
reqContext, cancel := context.WithTimeout(ctx, getRequestTimeout(opts.TimeoutOpts))
defer cancel()
resp, err := rs.doRequest(reqContext, rendererURL, opts.Headers)
if err != nil {
return nil, err
}
// save response to file
defer func() {
if err := resp.Body.Close(); err != nil {
rs.log.Warn("Failed to close response body", "err", err)
}
}()
err = rs.readFileResponse(reqContext, resp, filePath)
if err != nil {
return nil, err
}
return &RenderResult{FilePath: filePath}, nil
}
func (rs *RenderingService) renderCSVViaHTTP(ctx context.Context, renderKey string, opts CSVOpts) (*RenderCSVResult, error) {
filePath, err := rs.getNewFilePath(RenderCSV)
if err != nil {
return nil, err
}
rendererURL, err := url.Parse(rs.Cfg.RendererUrl + "/csv")
if err != nil {
return nil, err
}
queryParams := rendererURL.Query()
queryParams.Add("url", rs.getURL(opts.Path))
queryParams.Add("renderKey", renderKey)
queryParams.Add("domain", rs.domain)
queryParams.Add("timezone", isoTimeOffsetToPosixTz(opts.Timezone))
queryParams.Add("encoding", opts.Encoding)
queryParams.Add("timeout", strconv.Itoa(int(opts.Timeout.Seconds())))
rendererURL.RawQuery = queryParams.Encode()
// gives service some additional time to timeout and return possible errors.
reqContext, cancel := context.WithTimeout(ctx, getRequestTimeout(opts.TimeoutOpts))
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
defer cancel()
resp, err := rs.doRequest(reqContext, rendererURL, opts.Headers)
if err != nil {
return nil, err
}
// save response to file
defer func() {
if err := resp.Body.Close(); err != nil {
rs.log.Warn("Failed to close response body", "err", err)
}
}()
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
_, params, err := mime.ParseMediaType(resp.Header.Get("Content-Disposition"))
if err != nil {
return nil, err
}
downloadFileName := params["filename"]
err = rs.readFileResponse(reqContext, resp, filePath)
if err != nil {
return nil, err
}
return &RenderCSVResult{FilePath: filePath, FileName: downloadFileName}, nil
}
func (rs *RenderingService) doRequest(ctx context.Context, url *url.URL, headers map[string][]string) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, "GET", url.String(), nil)
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", rs.Cfg.BuildVersion))
for k, v := range headers {
req.Header[k] = v
}
rs.log.Debug("calling remote rendering service", "url", url)
// make request to renderer server
resp, err := netClient.Do(req)
if err != nil {
rs.log.Error("Failed to send request to remote rendering service", "error", err)
return nil, fmt.Errorf("failed to send request to remote rendering service: %w", err)
}
return resp, nil
}
func (rs *RenderingService) readFileResponse(ctx context.Context, resp *http.Response, filePath string) error {
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
// check for timeout first
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
rs.log.Info("Rendering timed out")
return ErrTimeout
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
}
// if we didn't get a 200 response, something went wrong.
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
if resp.StatusCode != http.StatusOK {
rs.log.Error("Remote rendering request failed", "error", resp.Status)
return fmt.Errorf("remote rendering request failed, status code: %d, status: %s", resp.StatusCode,
resp.Status)
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
}
out, err := os.Create(filePath)
if err != nil {
return err
}
defer func() {
if err := out.Close(); err != nil && !errors.Is(err, fs.ErrClosed) {
// We already close the file explicitly in the non-error path, so shouldn't be a problem
rs.log.Warn("Failed to close file", "path", filePath, "err", err)
}
}()
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
_, err = io.Copy(out, resp.Body)
if err != nil {
// check that we didn't timeout while receiving the response.
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
rs.log.Info("Rendering timed out")
return ErrTimeout
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
}
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
rs.log.Error("Remote rendering request failed", "error", err)
return fmt.Errorf("remote rendering request failed: %w", err)
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
7 years ago
}
if err := out.Close(); err != nil {
return fmt.Errorf("failed to write to %q: %w", filePath, err)
}
return nil
}
func (rs *RenderingService) getRemotePluginVersionWithRetry(callback func(string, error)) {
go func() {
var err error
for try := uint(0); try < remoteVersionFetchRetries; try++ {
version, err := rs.getRemotePluginVersion()
if err == nil {
callback(version, err)
return
}
rs.log.Info("Couldn't get remote renderer version, retrying", "err", err, "try", try)
time.Sleep(remoteVersionFetchInterval)
}
callback("", err)
}()
}
func (rs *RenderingService) getRemotePluginVersion() (string, error) {
rendererURL, err := url.Parse(rs.Cfg.RendererUrl + "/version")
if err != nil {
return "", err
}
headers := make(map[string][]string)
resp, err := rs.doRequest(context.Background(), rendererURL, headers)
if err != nil {
return "", err
}
defer func() {
if err := resp.Body.Close(); err != nil {
rs.log.Warn("Failed to close response body", "err", err)
}
}()
if resp.StatusCode == http.StatusNotFound {
// Old versions of the renderer lacked the version endpoint
return "1.0.0", nil
} else if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("remote rendering request to get version failed, status code: %d, status: %s", resp.StatusCode,
resp.Status)
}
var info struct {
Version string
}
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
return "", err
}
return info.Version, nil
}