Files
grafana/pkg/services/rendering/phantomjs.go
Marcus Efraimsson d0a80c59f3 Rendering: Store render key in remote cache (#22031)
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
2020-02-19 19:47:39 +01:00

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))
}