mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 12:42:15 +08:00

By storing render key in remote cache it will enable image renderer to use public facing url or load balancer url to render images and thereby remove the requirement of image renderer having to use the url of the originating Grafana instance when running HA setup (multiple Grafana instances). Fixes #17704 Ref grafana/grafana-image-renderer#91
130 lines
3.3 KiB
Go
130 lines
3.3 KiB
Go
package rendering
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
)
|
|
|
|
func (rs *RenderingService) renderViaPhantomJS(ctx context.Context, renderKey string, opts Opts) (*RenderResult, error) {
|
|
var executable = "phantomjs"
|
|
if runtime.GOOS == "windows" {
|
|
executable = executable + ".exe"
|
|
}
|
|
|
|
url := rs.getURL(opts.Path)
|
|
binPath, _ := filepath.Abs(filepath.Join(rs.Cfg.PhantomDir, executable))
|
|
if _, err := os.Stat(binPath); os.IsNotExist(err) {
|
|
rs.log.Error("executable not found", "executable", binPath)
|
|
return nil, ErrPhantomJSNotInstalled
|
|
}
|
|
|
|
scriptPath, _ := filepath.Abs(filepath.Join(rs.Cfg.PhantomDir, "render.js"))
|
|
pngPath, err := rs.getFilePathForNewImage()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
phantomDebugArg := "--debug=false"
|
|
if log.GetLogLevelFor("rendering") >= log.LvlDebug {
|
|
phantomDebugArg = "--debug=true"
|
|
}
|
|
|
|
cmdArgs := []string{
|
|
"--ignore-ssl-errors=true",
|
|
"--web-security=true",
|
|
"--local-url-access=false",
|
|
phantomDebugArg,
|
|
scriptPath,
|
|
fmt.Sprintf("url=%v", url),
|
|
fmt.Sprintf("width=%v", opts.Width),
|
|
fmt.Sprintf("height=%v", opts.Height),
|
|
fmt.Sprintf("png=%v", pngPath),
|
|
fmt.Sprintf("domain=%v", rs.domain),
|
|
fmt.Sprintf("timeout=%v", opts.Timeout.Seconds()),
|
|
fmt.Sprintf("renderKey=%v", renderKey),
|
|
}
|
|
|
|
if opts.Encoding != "" {
|
|
cmdArgs = append([]string{fmt.Sprintf("--output-encoding=%s", opts.Encoding)}, cmdArgs...)
|
|
}
|
|
|
|
// gives phantomjs some additional time to timeout and return possible errors.
|
|
commandCtx, cancel := context.WithTimeout(ctx, opts.Timeout+time.Second*2)
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(commandCtx, binPath, cmdArgs...)
|
|
cmd.Stderr = cmd.Stdout
|
|
|
|
timezone := ""
|
|
|
|
cmd.Env = os.Environ()
|
|
|
|
if opts.Timezone != "" {
|
|
timezone = isoTimeOffsetToPosixTz(opts.Timezone)
|
|
cmd.Env = appendEnviron(cmd.Env, "TZ", timezone)
|
|
}
|
|
|
|
// Added to disable usage of newer version of OPENSSL
|
|
// that seem to be incompatible with PhantomJS (used in Debian Buster)
|
|
if runtime.GOOS == "linux" {
|
|
disableNewOpenssl := "/etc/ssl"
|
|
cmd.Env = appendEnviron(cmd.Env, "OPENSSL_CONF", disableNewOpenssl)
|
|
}
|
|
|
|
rs.log.Debug("executing Phantomjs", "binPath", binPath, "cmdArgs", cmdArgs, "timezone", timezone)
|
|
|
|
out, err := cmd.Output()
|
|
|
|
if out != nil {
|
|
rs.log.Debug("Phantomjs output", "out", string(out))
|
|
}
|
|
|
|
if err != nil {
|
|
rs.log.Debug("Phantomjs error", "error", err)
|
|
}
|
|
|
|
// check for timeout first
|
|
if commandCtx.Err() == context.DeadlineExceeded {
|
|
rs.log.Info("Rendering timed out")
|
|
return nil, ErrTimeout
|
|
}
|
|
|
|
if err != nil {
|
|
rs.log.Error("Phantomjs exited with non zero exit code", "error", err)
|
|
return nil, err
|
|
}
|
|
|
|
rs.log.Debug("Image rendered", "path", pngPath)
|
|
return &RenderResult{FilePath: pngPath}, nil
|
|
}
|
|
|
|
func isoTimeOffsetToPosixTz(isoOffset string) string {
|
|
// invert offset
|
|
if strings.HasPrefix(isoOffset, "UTC+") {
|
|
return strings.Replace(isoOffset, "UTC+", "UTC-", 1)
|
|
}
|
|
if strings.HasPrefix(isoOffset, "UTC-") {
|
|
return strings.Replace(isoOffset, "UTC-", "UTC+", 1)
|
|
}
|
|
return isoOffset
|
|
}
|
|
|
|
func appendEnviron(baseEnviron []string, name string, value string) []string {
|
|
results := make([]string, 0)
|
|
prefix := fmt.Sprintf("%s=", name)
|
|
for _, v := range baseEnviron {
|
|
if !strings.HasPrefix(v, prefix) {
|
|
results = append(results, v)
|
|
}
|
|
}
|
|
return append(results, fmt.Sprintf("%s=%s", name, value))
|
|
}
|