Image Rendering: Remove PhantomJS support (#23460)

Removes all references and usage of PhantomJS #23375.
Remove direct link rendered image e2e smoke test for now.
Docker: Fix installing chrome in ubuntu custom docker image.
Improve handling of image renderer not available/installed #23593.
Add PhantomJS breaking change and upgrading notes.
Use grabpl v0.2.10.

Closes #13802

Co-authored-by: Kyle Brandt <kyle@grafana.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
This commit is contained in:
Marcus Efraimsson 2020-04-15 22:17:41 +02:00 committed by GitHub
parent 0a1ab60b8c
commit 6e313e7d37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 107 additions and 478 deletions

View File

@ -50,7 +50,7 @@ commands:
- run:
name: "Install Grafana build pipeline tool"
command: |
VERSION=0.2.9
VERSION=0.2.10
curl -fLO https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v${VERSION}/grabpl
chmod +x grabpl
mv grabpl /tmp

View File

@ -1,3 +1,9 @@
# 7.0.0 (unreleased)
## Breaking changes
- **Removed PhantomJS**: PhantomJS was deprecated in [Grafana v6.4](https://grafana.com/docs/grafana/latest/guides/whats-new-in-v6-4/#phantomjs-deprecation) and starting from Grafana v7.0.0, all PhantomJS support has been removed. This means that Grafana no longer ships with a built-in image renderer, and we advise you to install the [Grafana Image Renderer plugin](https://grafana.com/grafana/plugins/grafana-image-renderer).
# 6.7.2 (2020-04-02)
### Bug Fixes
@ -6,7 +12,7 @@
- **Dashboard**: Fixed issue with saving new dashboard after changing title . [#23104](https://github.com/grafana/grafana/pull/23104), [@dprokop](https://github.com/dprokop)
- **DataLinks**: make sure we use the correct datapoint when dataset contains null value.. [#22981](https://github.com/grafana/grafana/pull/22981), [@mckn](https://github.com/mckn)
- **Plugins**: Fixed issue for plugins that imported dateMath util . [#23069](https://github.com/grafana/grafana/pull/23069), [@mckn](https://github.com/mckn)
- **Security**: Fix for dashboard snapshot original dashboard link could contain XSS vulnerability in url. [#23254](https://github.com/grafana/grafana/pull/23254), [@torkelo](https://github.com/torkelo). Big thanks to Ahmed A. Sherif for reporting this issue.
- **Security**: Fix for dashboard snapshot original dashboard link could contain XSS vulnerability in url. [#23254](https://github.com/grafana/grafana/pull/23254), [@torkelo](https://github.com/torkelo). Big thanks to Ahmed A. Sherif for reporting this issue.
- **Variables**: Fixes issue with too many queries being issued for nested template variables after value change. [#23220](https://github.com/grafana/grafana/pull/23220), [@torkelo](https://github.com/torkelo)
- **Plugins**: Expose promiseToDigest. [#23249](https://github.com/grafana/grafana/pull/23249), [@torkelo](https://github.com/torkelo)
- **Reporting (Enterprise)**: Fixes issue updating a report created by someone else

View File

@ -18,16 +18,6 @@ RUN go run build.go build
# Node build container
FROM node:12.13.0-alpine
# PhantomJS
RUN apk add --no-cache curl &&\
cd /tmp && curl -Ls https://github.com/dustinblackman/phantomized/releases/download/2.1.1/dockerized-phantomjs.tar.gz | tar xz &&\
cp -R lib lib64 / &&\
cp -R usr/lib/x86_64-linux-gnu /usr/lib &&\
cp -R usr/share /usr/share &&\
cp -R etc/fonts /etc &&\
curl -k -Ls https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 | tar -jxf - &&\
cp phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs
WORKDIR /usr/src/app/
COPY package.json yarn.lock ./
@ -80,18 +70,9 @@ RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \
chmod -R 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING"
# PhantomJS
COPY --from=1 /tmp/lib /lib
COPY --from=1 /tmp/lib64 /lib64
COPY --from=1 /tmp/usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu
COPY --from=1 /tmp/usr/share /usr/share
COPY --from=1 /tmp/etc/fonts /etc/fonts
COPY --from=1 /usr/local/bin/phantomjs /usr/local/bin
COPY --from=0 /go/src/github.com/grafana/grafana/bin/linux-amd64/grafana-server /go/src/github.com/grafana/grafana/bin/linux-amd64/grafana-cli ./bin/
COPY --from=1 /usr/src/app/public ./public
COPY --from=1 /usr/src/app/tools ./tools
COPY tools/phantomjs/render.js ./tools/phantomjs/render.js
EXPOSE 3000

View File

@ -14,11 +14,6 @@ RUN go run build.go build
FROM node:12.13 AS js-builder
# PhantomJS
RUN apt-get update && apt-get install -y curl &&\
curl -L https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 | tar xj &&\
cp phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs
WORKDIR /usr/src/app/
COPY package.json yarn.lock ./
@ -54,8 +49,8 @@ WORKDIR $GF_PATHS_HOME
COPY conf conf
# We need font libs for phantomjs, and curl should be part of the image
RUN apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates libfontconfig1 curl
# curl should be part of the image
RUN apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates curl
RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
addgroup --system --gid $GF_GID grafana && \
@ -71,14 +66,11 @@ RUN mkdir -p "$GF_PATHS_HOME/.aws" && \
chown -R grafana:grafana "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING" && \
chmod -R 777 "$GF_PATHS_DATA" "$GF_PATHS_HOME/.aws" "$GF_PATHS_LOGS" "$GF_PATHS_PLUGINS" "$GF_PATHS_PROVISIONING"
# PhantomJS
COPY --from=js-builder /usr/local/bin/phantomjs /usr/local/bin/
COPY --from=go-builder /src/grafana/bin/linux-amd64/grafana-server /src/grafana/bin/linux-amd64/grafana-cli bin/
COPY --from=js-builder /usr/src/app/public public
COPY --from=js-builder /usr/src/app/tools tools
COPY tools/phantomjs/render.js tools/phantomjs/
COPY packaging/docker/run.sh /
USER grafana

View File

@ -35,7 +35,6 @@ module.exports = function (grunt) {
config.libc = grunt.option('libc');
}
config.phjs = grunt.option('phjsToRelease');
config.pkg.version = grunt.option('pkgVer') || config.pkg.version;
console.log('Version', config.pkg.version);

View File

@ -40,7 +40,6 @@ var (
linuxPackageVersion string = "v1"
linuxPackageIteration string = ""
race bool
phjsToRelease string
workingDir string
includeBuildId bool = true
buildId string = "0"
@ -69,7 +68,6 @@ func main() {
flag.StringVar(&libc, "libc", "", "LIBC")
flag.BoolVar(&cgo, "cgo-enabled", cgo, "Enable cgo")
flag.StringVar(&pkgArch, "pkg-arch", "", "PKG ARCH")
flag.StringVar(&phjsToRelease, "phjs", "", "PhantomJS binary")
flag.BoolVar(&race, "race", race, "Use race detector")
flag.BoolVar(&modVendor, "modVendor", modVendor, "Go modules use vendor folder")
flag.BoolVar(&includeBuildId, "includeBuildId", includeBuildId, "IncludeBuildId in package name")
@ -459,9 +457,6 @@ func gruntBuildArg(task string) []string {
if libc != "" {
args = append(args, fmt.Sprintf("--libc=%s", libc))
}
if phjsToRelease != "" {
args = append(args, fmt.Sprintf("--phjsToRelease=%v", phjsToRelease))
}
if enterprise {
args = append(args, "--enterprise")
}

View File

@ -108,7 +108,7 @@ The following example describes how to build and run the remote HTTP rendering s
## PhantomJS
> PhantomJS is deprecated since Grafana v6.4 and will be removed in a future release. Please migrate to the Grafana Image Renderer plugin or remote rendering service.
> Starting from Grafana v7.0.0, all PhantomJS support has been removed. Please use the Grafana Image Renderer plugin or remote rendering service.
## Troubleshoot image rendering

View File

@ -238,3 +238,9 @@ Due to this change in Chrome, the `[security]` setting `cookie_samesite` configu
This version of Chrome also rejects insecure `SameSite=None` cookies. See https://www.chromestatus.com/feature/5633521622188032 for more information. Make sure that you
change the `[security]` setting `cookie_secure` to `true` and use HTTPS when `cookie_samesite` is configured to `none`, otherwise authentication in Grafana won't work properly.
## Upgrading to v7.0
### PhantomJS removed
PhantomJS was deprecated in [Grafana v6.4](https://grafana.com/docs/grafana/latest/guides/whats-new-in-v6-4/#phantomjs-deprecation) and starting from Grafana v7.0.0, all PhantomJS support has been removed. This means that Grafana no longer ships with a built-in image renderer, and we adwise you to install the [Grafana Image Renderer plugin](https://grafana.com/grafana/plugins/grafana-image-renderer).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -21,8 +21,6 @@ e2e.scenario({
.contains('Show')
.click();
e2e.pages.Components.BackButton.backArrow().click();
// e2e.pages.Dashboard.Panels.Panel.title('Panel Title').click();
// e2e.pages.Dashboard.Panels.Panel.headerItems('Inspect').click();
},

View File

@ -172,7 +172,6 @@
"ngtemplate-loader": "2.0.1",
"node-sass": "4.13.1",
"optimize-css-assets-webpack-plugin": "5.0.3",
"phantomjs-prebuilt": "2.1.16",
"pixelmatch": "5.1.0",
"pngjs": "3.4.0",
"postcss-browser-reporter": "0.6.0",

View File

@ -104,7 +104,7 @@ export class GrafanaBootConfig {
tracingIntegration: false,
};
licenseInfo: LicenseInfo = {} as LicenseInfo;
phantomJSRenderer = false;
rendererAvailable = false;
constructor(options: GrafanaBootConfig) {
this.theme = options.bootData.user.lightTheme ? getTheme(GrafanaThemeType.Light) : getTheme(GrafanaThemeType.Dark);

View File

@ -41,19 +41,6 @@ RUN if [ `arch` = "x86_64" ]; then \
rm -f /etc/ld.so.cache; \
fi
# PhantomJS
RUN if [ `arch` = "x86_64" ]; then \
apk add --no-cache --virtual phantomjs-utils curl && \
cd /tmp && \
curl -Ls https://github.com/dustinblackman/phantomized/releases/download/2.1.1/dockerized-phantomjs.tar.gz | tar xz && \
cp -R lib lib64 / && \
cp -R usr/lib/x86_64-linux-gnu /usr/lib && \
cp -R usr/share/fonts /usr/share && \
cp -R etc/fonts /etc && \
rm -rf /tmp/* && \
apk del --no-cache phantomjs-utils; \
fi
COPY --from=0 /tmp/grafana "$GF_PATHS_HOME"
RUN mkdir -p "$GF_PATHS_HOME/.aws" && \

View File

@ -17,15 +17,17 @@ RUN mkdir -p "$GF_PATHS_PLUGINS" && \
RUN if [ $GF_INSTALL_IMAGE_RENDERER_PLUGIN = "true" ]; then \
apt-get update && \
apt-get upgrade -y && \
apt-get install -y chromium-browser && \
apt-get install -y gdebi-core && \
cd /tmp && \
curl -LO https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \
gdebi --n google-chrome-stable_current_amd64.deb && \
apt-get autoremove -y && \
rm -rf /var/lib/apt/lists/* && \
rm -rf /usr/share/grafana/tools/phantomjs; \
rm -rf /var/lib/apt/lists/*; \
fi
USER grafana
ENV GF_RENDERER_PLUGIN_CHROME_BIN="/usr/bin/chromium-browser"
ENV GF_RENDERER_PLUGIN_CHROME_BIN="/usr/bin/google-chrome"
RUN if [ $GF_INSTALL_IMAGE_RENDERER_PLUGIN = "true" ]; then \
grafana-cli \

View File

@ -27,14 +27,9 @@ ENV PATH=/usr/share/grafana/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bi
WORKDIR $GF_PATHS_HOME
# Install dependencies
# We need curl in the image, and if the architecture is x86-64, we need to install libfontconfig1 for PhantomJS
RUN if [ `arch` = "x86_64" ]; then \
apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates libfontconfig1 curl && \
apt-get autoremove -y && rm -rf /var/lib/apt/lists/*; \
else \
apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates curl && \
apt-get autoremove -y && rm -rf /var/lib/apt/lists/*; \
fi
# We need curl in the image
RUN apt-get update && apt-get upgrade -y && apt-get install -y ca-certificates curl && \
apt-get autoremove -y && rm -rf /var/lib/apt/lists/*;
COPY --from=grafana-builder /tmp/grafana "$GF_PATHS_HOME"

View File

@ -4,7 +4,6 @@ import (
"strconv"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/rendering"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/util"
@ -212,7 +211,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"licenseUrl": hs.License.LicenseURL(c.SignedInUser),
},
"featureToggles": hs.Cfg.FeatureToggles,
"phantomJSRenderer": rendering.IsPhantomJSEnabled,
"rendererAvailable": hs.RenderService.IsAvailable(),
}
return jsonObj, nil

View File

@ -42,7 +42,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
appURL := setting.AppUrl
appSubURL := setting.AppSubUrl
// special case when doing localhost call from phantomjs
// special case when doing localhost call from image renderer
if c.IsRenderCall && !hs.Cfg.ServeFromSubPath {
appURL = fmt.Sprintf("%s://localhost:%s", setting.Protocol, setting.HttpPort)
appSubURL = ""

View File

@ -53,19 +53,25 @@ func (n *notificationService) SendIfNeeded(evalCtx *EvalContext) error {
}
if notifierStates.ShouldUploadImage() {
// Create a copy of EvalContext and give it a new, shorter, timeout context to upload the image
uploadEvalCtx := *evalCtx
timeout := setting.AlertingNotificationTimeout / 2
var uploadCtxCancel func()
uploadEvalCtx.Ctx, uploadCtxCancel = context.WithTimeout(evalCtx.Ctx, timeout)
if n.renderService.IsAvailable() {
// Create a copy of EvalContext and give it a new, shorter, timeout context to upload the image
uploadEvalCtx := *evalCtx
timeout := setting.AlertingNotificationTimeout / 2
var uploadCtxCancel func()
uploadEvalCtx.Ctx, uploadCtxCancel = context.WithTimeout(evalCtx.Ctx, timeout)
// Try to upload the image without consuming all the time allocated for EvalContext
if err = n.renderAndUploadImage(&uploadEvalCtx, timeout); err != nil {
n.log.Error("Failed to render and upload alert panel image.", "ruleId", uploadEvalCtx.Rule.ID, "error", err)
// Try to upload the image without consuming all the time allocated for EvalContext
if err = n.renderAndUploadImage(&uploadEvalCtx, timeout); err != nil {
n.log.Error("Failed to render and upload alert panel image.", "ruleId", uploadEvalCtx.Rule.ID, "error", err)
}
uploadCtxCancel()
evalCtx.ImageOnDiskPath = uploadEvalCtx.ImageOnDiskPath
evalCtx.ImagePublicURL = uploadEvalCtx.ImagePublicURL
} else {
n.log.Warn("Could not render image for alert notification, 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/")
}
uploadCtxCancel()
evalCtx.ImageOnDiskPath = uploadEvalCtx.ImageOnDiskPath
evalCtx.ImagePublicURL = uploadEvalCtx.ImagePublicURL
}
return n.sendNotifications(evalCtx, notifierStates)

View File

@ -39,6 +39,16 @@ func TestNotificationService(t *testing.T) {
require.Truef(t, evalCtx.Ctx.Value(notificationSent{}).(bool), "expected notification to be sent, but wasn't")
})
notificationServiceScenario(t, "Given alert rule with upload image enabled but no renderer available should not render and upload image, but send notification", evalCtx, true, func(scenarioCtx *scenarioContext) {
scenarioCtx.rendererAvailable = false
err := scenarioCtx.notificationService.SendIfNeeded(evalCtx)
require.NoError(t, err)
require.Equalf(t, 0, scenarioCtx.renderCount, "expected render to not be called, but it was")
require.Equalf(t, 0, scenarioCtx.imageUploadCount, "expected image to not be uploaded, but it was")
require.Truef(t, evalCtx.Ctx.Value(notificationSent{}).(bool), "expected notification to be sent, but wasn't")
})
notificationServiceScenario(t, "Given alert rule with upload image disabled should not render and upload image, but send notification", evalCtx, false, func(scenarioCtx *scenarioContext) {
err := scenarioCtx.notificationService.SendIfNeeded(evalCtx)
require.NoError(t, err)
@ -114,6 +124,7 @@ type scenarioContext struct {
renderCount int
uploadProvider func(ctx context.Context, path string) (string, error)
renderProvider func(ctx context.Context, opts rendering.Opts) (*rendering.RenderResult, error)
rendererAvailable bool
}
type scenarioFunc func(c *scenarioContext)
@ -197,7 +208,12 @@ func notificationServiceScenario(t *testing.T, name string, evalCtx *EvalContext
return &rendering.RenderResult{FilePath: "image.png"}, nil
}
scenarioCtx.rendererAvailable = true
renderService := &testRenderService{
isAvailableProvider: func() bool {
return scenarioCtx.rendererAvailable
},
renderProvider: func(ctx context.Context, opts rendering.Opts) (*rendering.RenderResult, error) {
if scenarioCtx.renderProvider != nil {
if _, err := scenarioCtx.renderProvider(ctx, opts); err != nil {
@ -286,10 +302,19 @@ func (n *testNotifier) GetFrequency() time.Duration {
var _ Notifier = &testNotifier{}
type testRenderService struct {
isAvailableProvider func() bool
renderProvider func(ctx context.Context, opts rendering.Opts) (*rendering.RenderResult, error)
renderErrorImageProvider func(error error) (*rendering.RenderResult, error)
}
func (s *testRenderService) IsAvailable() bool {
if s.isAvailableProvider != nil {
return s.isAvailableProvider()
}
return true
}
func (s *testRenderService) Render(ctx context.Context, opts rendering.Opts) (*rendering.RenderResult, error) {
if s.renderProvider != nil {
return s.renderProvider(ctx, opts)

View File

@ -32,6 +32,7 @@ type RenderResult struct {
type renderFunc func(ctx context.Context, renderKey string, options Opts) (*RenderResult, error)
type Service interface {
IsAvailable() bool
Render(ctx context.Context, opts Opts) (*RenderResult, error)
RenderErrorImage(error error) (*RenderResult, error)
GetRenderUser(key string) (*RenderUser, bool)

View File

@ -1,129 +0,0 @@
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))
}

View File

@ -6,6 +6,7 @@ import (
"net/url"
"os"
"path/filepath"
"strings"
"time"
"github.com/grafana/grafana/pkg/infra/remotecache"
@ -23,8 +24,6 @@ func init() {
registry.RegisterService(&RenderingService{})
}
var IsPhantomJSEnabled = false
const renderKeyPrefix = "render-%s"
type RenderUser struct {
@ -76,30 +75,31 @@ func (rs *RenderingService) Run(ctx context.Context) error {
return nil
}
if plugins.Renderer == nil {
rs.log = rs.log.New("renderer", "phantomJS")
rs.log.Info("Backend rendering via phantomJS")
rs.log.Warn("phantomJS is deprecated and will be removed in a future release. " +
"You should consider migrating from phantomJS to grafana-image-renderer plugin. " +
"Read more at https://grafana.com/docs/grafana/latest/administration/image_rendering/")
rs.renderAction = rs.renderViaPhantomJS
IsPhantomJSEnabled = true
if plugins.Renderer != nil {
rs.log = rs.log.New("renderer", "plugin")
rs.pluginInfo = plugins.Renderer
if err := rs.startPlugin(ctx); err != nil {
return err
}
rs.renderAction = rs.renderViaPlugin
<-ctx.Done()
return nil
}
rs.log = rs.log.New("renderer", "plugin")
rs.pluginInfo = plugins.Renderer
rs.log.Debug("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 err := rs.startPlugin(ctx); err != nil {
return err
}
rs.renderAction = rs.renderViaPlugin
<-ctx.Done()
return nil
}
func (rs *RenderingService) IsAvailable() bool {
return rs.renderAction != nil
}
func (rs *RenderingService) RenderErrorImage(err error) (*RenderResult, error) {
imgUrl := "public/img/rendering_error.png"
@ -209,3 +209,14 @@ func (rs *RenderingService) deleteRenderKey(key string) {
rs.log.Error("Failed to delete render key", "error", err)
}
}
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
}

View File

@ -239,7 +239,6 @@ type Cfg struct {
// Rendering
ImagesDir string
PhantomDir string
RendererUrl string
RendererCallbackUrl string
RendererLimit int
@ -940,7 +939,6 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
}
}
cfg.ImagesDir = filepath.Join(cfg.DataPath, "png")
cfg.PhantomDir = filepath.Join(HomePath, "tools/phantomjs")
cfg.TempDataLifetime = iniFile.Section("paths").Key("temp_data_lifetime").MustDuration(time.Second * 3600 * 24)
cfg.MetricsEndpointEnabled = iniFile.Section("metrics").Key("enabled").MustBool(true)
cfg.MetricsEndpointBasicAuthUsername, err = valueAsString(iniFile.Section("metrics"), "basic_auth_username", "")

View File

@ -17,7 +17,7 @@ export class Profiler {
renderingCompleted() {
// add render counter to root scope
// used by phantomjs render.js to know when panel has rendered
// used by image renderer to know when panel has rendered
this.panelsRendered = (this.panelsRendered || 0) + 1;
// this window variable is used by backend rendering tools to know

View File

@ -6,6 +6,7 @@ import { SelectableValue, PanelModel, AppEvents } from '@grafana/data';
import { DashboardModel } from 'app/features/dashboard/state';
import { buildImageUrl, buildShareUrl } from './utils';
import { appEvents } from 'app/core/core';
import config from 'app/core/config';
const themeOptions: Array<SelectableValue<string>> = [
{ label: 'current', value: 'current' },
@ -127,7 +128,7 @@ export class ShareLink extends PureComponent<Props, State> {
</div>
</div>
</div>
{panel && (
{panel && config.rendererAvailable && (
<div className="gf-form">
<a href={imageUrl} target="_blank" aria-label={selectors.linkToRenderedImage}>
<Icon name="camera" /> Direct link rendered image

View File

@ -255,7 +255,7 @@ export class PanelChrome extends PureComponent<Props, State> {
const { theme } = config;
// This is only done to increase a counter that is used by backend
// image rendering (phantomjs/headless chrome) to know when to capture image
// image rendering to know when to capture image
const loading = data.state;
if (loading === LoadingState.Done) {
profiler.renderingCompleted();

View File

@ -166,11 +166,6 @@ export function grafanaAppDirective(
elem.toggleClass('view-mode--playlist', false);
});
// check if we are in server side render
if (config.phantomJSRenderer && document.cookie.indexOf('renderKey') !== -1) {
body.addClass('body--phantomjs');
}
// tooltip removal fix
// manage page classes
let pageClass: string;

View File

@ -118,29 +118,6 @@
padding-left: 6px;
}
// fix for phantomjs
.body--phantomjs {
.graph-panel {
display: -webkit-box;
}
.graph-panel--legend-right {
.graph-legend {
display: block;
max-width: min-content;
}
.graph-panel__chart {
display: flex;
}
.graph-legend-table {
display: table;
width: auto;
}
}
}
.graph-legend-table {
padding-bottom: 1px;
padding-right: 5px;

View File

@ -275,11 +275,6 @@
opacity: 0.9;
}
// fix for phantomjs
.body--phantomjs .baron__track .baron__bar {
opacity: 0 !important;
}
.baron__control {
display: none;
}

View File

@ -86,13 +86,6 @@
}
}
// fix for phantomjs
.body--phantomjs {
.scroll-canvas {
overflow: hidden;
}
}
.page-body {
padding-top: $spacer * 2;
padding-bottom: $spacer * 4;

View File

@ -90,8 +90,6 @@ source /etc/profile.d/rvm.sh
echo "Packaging"
go run build.go -goos linux -pkg-arch amd64 ${OPT} package-only
go run build.go -goos linux -pkg-arch amd64 -libc musl ${OPT} -skipRpm -skipDeb package-only
#removing amd64 phantomjs bin for armv7/arm64 packages
rm tools/phantomjs/phantomjs
go run build.go -goos linux -pkg-arch armv6 ${OPT} -skipRpm package-only
go run build.go -goos linux -pkg-arch armv7 ${OPT} package-only
@ -99,20 +97,8 @@ go run build.go -goos linux -pkg-arch arm64 ${OPT} package-only
go run build.go -goos linux -pkg-arch armv7 -libc musl ${OPT} -skipRpm -skipDeb package-only
go run build.go -goos linux -pkg-arch arm64 -libc musl ${OPT} -skipRpm -skipDeb package-only
if [ -d '/tmp/phantomjs/darwin' ]; then
cp /tmp/phantomjs/darwin/phantomjs tools/phantomjs/phantomjs
else
echo 'PhantomJS binaries for darwin missing!'
fi
go run build.go -goos darwin -pkg-arch amd64 ${OPT} package-only
if [ -d '/tmp/phantomjs/windows' ]; then
cp /tmp/phantomjs/windows/phantomjs.exe tools/phantomjs/phantomjs.exe
rm tools/phantomjs/phantomjs || true
else
echo 'PhantomJS binaries for Windows missing!'
fi
cp /usr/local/go/lib/time/zoneinfo.zip tools/zoneinfo.zip
go run build.go -goos windows -pkg-arch amd64 ${OPT} package-only
rm tools/zoneinfo.zip

View File

@ -1,24 +0,0 @@
#!/bin/bash
set -eo pipefail
if [ ! -d '/tmp/phantomjs' ]; then
_version="2.1.1"
curl -fL https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-$_version-windows.zip -o /tmp/phantomjs-win.zip
[ "$(sha256sum /tmp/phantomjs-win.zip | cut -d' ' -f1)" = \
"d9fb05623d6b26d3654d008eab3adafd1f6350433dfd16138c46161f42c7dcc8" ] || \
(echo "Checksum mismatch phantomjs-$_version-windows.zip"; exit 1)
curl -fL https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-$_version-macosx.zip -o /tmp/phantomjs-mac.zip
[ "$(sha256sum /tmp/phantomjs-mac.zip | cut -d' ' -f1)" = \
"538cf488219ab27e309eafc629e2bcee9976990fe90b1ec334f541779150f8c1" ] || \
(echo "Checksum mismatch phantomjs-$_version-mac.zip"; exit 1)
cd /tmp
unzip /tmp/phantomjs-win.zip
unzip /tmp/phantomjs-mac.zip
mkdir -p /tmp/phantomjs/windows /tmp/phantomjs/darwin
cp /tmp/phantomjs-$_version-windows/bin/phantomjs.exe /tmp/phantomjs/windows/phantomjs.exe
cp /tmp/phantomjs-$_version-macosx/bin/phantomjs /tmp/phantomjs/darwin/phantomjs
fi

View File

@ -6,7 +6,6 @@ module.exports = function(grunt) {
grunt.registerTask('build', [
'clean:release',
'clean:build',
'phantomjs',
'exec:webpack',
]);

View File

@ -5,7 +5,6 @@ module.exports = function(grunt) {
// prettier-ignore
grunt.registerTask('default', [
'clean:build',
'phantomjs',
]);
// prettier-ignore

View File

@ -1,33 +0,0 @@
module.exports = function(config,grunt) {
'use strict';
grunt.registerTask('phantomjs', 'Copy phantomjs binary to vendor/', function() {
var dest = './tools/phantomjs/phantomjs';
var confDir = './node_modules/phantomjs-prebuilt/lib/';
if (process.platform === "win32") {
dest += ".exe";
}
var src = config.phjs;
if (!src) {
var m = grunt.file.read(confDir+"location.js");
src = /= \"([^\"]*)\"/.exec(m)[1];
if (!grunt.file.isPathAbsolute(src)) {
src = confDir+src;
}
}
try {
grunt.config('copy.phantom_bin', {
src: src,
dest: dest,
options: { mode: true},
});
grunt.task.run('copy:phantom_bin');
} catch (err) {
grunt.verbose.writeln(err);
grunt.fail.warn('No working Phantomjs binary available')
}
});
};

0
tools/.gitkeep Normal file
View File

View File

@ -1,86 +0,0 @@
(function() {
'use strict';
var page = require('webpage').create();
var args = require('system').args;
var params = {};
var regexp = /^([^=]+)=([^$]+)/;
args.forEach(function(arg) {
var parts = arg.match(regexp);
if (!parts) {
return;
}
params[parts[1]] = parts[2];
});
var usage = 'url=<url> png=<filename> width=<width> height=<height> renderKey=<key>';
if (!params.url || !params.png || !params.renderKey || !params.domain) {
console.log(usage);
phantom.exit();
}
phantom.addCookie({
name: 'renderKey',
value: params.renderKey,
domain: params.domain,
});
page.viewportSize = {
width: params.width || '800',
height: params.height || '400',
};
var timeoutMs = (parseInt(params.timeout) || 10) * 1000;
var waitBetweenReadyCheckMs = 50;
var totalWaitMs = 0;
page.open(params.url, function(status) {
console.log('Loading a web page: ' + params.url + ' status: ' + status, timeoutMs);
page.onError = function(msg, trace) {
var msgStack = ['ERROR: ' + msg];
if (trace && trace.length) {
msgStack.push('TRACE:');
trace.forEach(function(t) {
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function + '")' : ''));
});
}
console.error(msgStack.join('\n'));
};
function checkIsReady() {
var panelsRendered = page.evaluate(function() {
var panelCount = document.querySelectorAll('plugin-component').length;
return window.panelsRendered >= panelCount;
});
if (panelsRendered || totalWaitMs > timeoutMs) {
var bb = page.evaluate(function() {
var container = document.getElementsByClassName('dashboard-container');
if (container.length == 0) {
container = document.getElementsByClassName('panel-container');
}
return container[0].getBoundingClientRect();
});
// reset viewport to render full page
page.viewportSize = {
width: bb.width,
height: bb.height,
};
setTimeout(function() {
page.render(params.png);
phantom.exit();
}, 5);
} else {
totalWaitMs += waitBetweenReadyCheckMs;
setTimeout(checkIsReady, waitBetweenReadyCheckMs);
}
}
setTimeout(checkIsReady, waitBetweenReadyCheckMs);
});
})();

View File

@ -11923,7 +11923,7 @@ extglob@^2.0.4:
snapdragon "^0.8.1"
to-regex "^3.0.1"
extract-zip@1.6.7, extract-zip@^1.6.5, extract-zip@^1.6.6:
extract-zip@1.6.7, extract-zip@^1.6.6:
version "1.6.7"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9"
integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=
@ -12611,15 +12611,6 @@ fs-extra@^0.30.0:
path-is-absolute "^1.0.0"
rimraf "^2.2.8"
fs-extra@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950"
integrity sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=
dependencies:
graceful-fs "^4.1.2"
jsonfile "^2.1.0"
klaw "^1.0.0"
fs-extra@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291"
@ -13567,14 +13558,6 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
hasha@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1"
integrity sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=
dependencies:
is-stream "^1.0.1"
pinkie-promise "^2.0.0"
hast-to-hyperscript@^7.0.0:
version "7.0.3"
resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-7.0.3.tgz#e36b2a32b237f83bbb80165351398226f12b7d6e"
@ -15752,11 +15735,6 @@ just-extend@^4.0.2:
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.0.2.tgz#f3f47f7dfca0f989c55410a7ebc8854b07108afc"
integrity sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==
kew@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b"
integrity sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=
killable@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
@ -18605,21 +18583,6 @@ performance-now@^2.1.0:
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
phantomjs-prebuilt@2.1.16:
version "2.1.16"
resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz#efd212a4a3966d3647684ea8ba788549be2aefef"
integrity sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=
dependencies:
es6-promise "^4.0.3"
extract-zip "^1.6.5"
fs-extra "^1.0.0"
hasha "^2.2.0"
kew "^0.7.0"
progress "^1.1.8"
request "^2.81.0"
request-progress "^2.0.1"
which "^1.2.10"
picomatch@^2.0.4, picomatch@^2.0.7:
version "2.2.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a"
@ -21577,13 +21540,6 @@ request-progress@3.0.0:
dependencies:
throttleit "^1.0.0"
request-progress@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08"
integrity sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=
dependencies:
throttleit "^1.0.0"
request-promise-core@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346"
@ -21600,7 +21556,7 @@ request-promise-native@^1.0.5:
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
request@2.88.0, "request@>=2.76.0 <3.0.0", request@^2.55.0, request@^2.81.0, request@^2.83.0, request@^2.87.0, request@^2.88.0:
request@2.88.0, "request@>=2.76.0 <3.0.0", request@^2.55.0, request@^2.83.0, request@^2.87.0, request@^2.88.0:
version "2.88.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
@ -25300,7 +25256,7 @@ which-pm-runs@^1.0.0:
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=
which@1, which@1.3.1, which@^1.2.10, which@^1.2.14, which@^1.2.4, which@^1.2.9, which@^1.3.0, which@^1.3.1, which@~1.3.0:
which@1, which@1.3.1, which@^1.2.14, which@^1.2.4, which@^1.2.9, which@^1.3.0, which@^1.3.1, which@~1.3.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==