Skip to content

Commit a43023c

Browse files
authored
feat(go): reach rendering parity (#753)
1 parent b7d13e0 commit a43023c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2788
-1312
lines changed

.github/workflows/docker-test.yaml

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,26 @@ concurrency:
1515
cancel-in-progress: true
1616

1717
jobs:
18-
bats:
18+
acceptance:
1919
strategy:
2020
fail-fast: false
2121
matrix:
2222
file: [Dockerfile, go.Dockerfile]
2323
# TODO: Use self-hosted runners when available.
2424
runs-on: [github-hosted-ubuntu-arm64-large, github-hosted-ubuntu-x64-large]
2525

26-
name: bats (${{ matrix.runs-on }}, ${{ matrix.file }})
26+
name: acceptance tests (${{ matrix.runs-on }}, ${{ matrix.file }})
2727
runs-on: ${{ matrix.runs-on }}
2828
permissions:
2929
contents: read # clone the repository
3030
steps:
3131
- uses: actions/checkout@v4
3232
with:
3333
persist-credentials: false
34-
- uses: bats-core/bats-action@42fcc8700f773c075a16a90eb11674c0318ad507 # 3.0.1
34+
- uses: actions/setup-go@v5
35+
with:
36+
go-version-file: go.mod
37+
cache: false
3538

3639
- name: docker build
3740
env:
@@ -40,8 +43,45 @@ jobs:
4043
BUILDKIT_STEP_LOG_MAX_SPEED: -1
4144
FILE: ${{ matrix.file }}
4245
run: docker build . -t image-renderer -f "$FILE"
43-
- name: bats
44-
working-directory: tests/bats
45-
run: ./test.sh image-renderer
46+
- name: go test ./tests/acceptance/...
47+
run: go test ./tests/acceptance/... -count=1
4648
env:
4749
TERM: linux
50+
IMAGE: image-renderer
51+
UPDATE_FIXTURES: 'true' # this will make changes, so we can see them
52+
53+
- name: Tar changed files
54+
if: failure()
55+
id: tar
56+
run: |
57+
set -euo pipefail
58+
readarray -t files < <(git diff --name-only)
59+
if [ "${#files[@]}" -gt "0" ]; then
60+
tar cJf changed-files.tar.xz "${files[@]}"
61+
echo 'upload=true' >> "$GITHUB_OUTPUT"
62+
else
63+
echo "No files changed :)"
64+
fi
65+
- name: Upload changed files
66+
if: ${{ steps.tar.outputs.upload == 'true' && (success() || failure()) }}
67+
uses: actions/upload-artifact@v4
68+
with:
69+
name: changed-files-${{ matrix.file }}-${{ matrix.runs-on }}
70+
path: changed-files.tar.xz
71+
72+
# This is the job that is actually required by rulesets.
73+
required-acceptance-tests:
74+
needs:
75+
- acceptance
76+
# always() is the best function here.
77+
# success() || failure() will skip this function if any need is also skipped.
78+
# That means conditional test suites will fail the entire requirement check.
79+
if: always()
80+
81+
name: All acceptance tests complete
82+
runs-on: ubuntu-latest
83+
steps:
84+
- name: Assert no failures
85+
env:
86+
NEEDS: ${{ toJson(needs) }}
87+
run: exit $(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | length')

cmd/root.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ import (
88
"github.com/grafana/grafana-image-renderer/cmd/config"
99
"github.com/grafana/grafana-image-renderer/cmd/healthcheck"
1010
"github.com/grafana/grafana-image-renderer/cmd/server"
11-
"github.com/grafana/grafana-image-renderer/pkg/version"
11+
"github.com/grafana/grafana-image-renderer/pkg/service"
1212
"github.com/urfave/cli/v3"
1313
)
1414

1515
func NewRootCmd() *cli.Command {
1616
return &cli.Command{
1717
Name: "grafana-image-renderer",
1818
Usage: "A service for Grafana to render images and documents from Grafana websites.",
19-
Version: version.ServiceVersion(),
19+
Version: service.NewVersionService().GetPrettyVersion(),
2020
Flags: []cli.Flag{
2121
&cli.StringFlag{
2222
Name: "log-level",

cmd/server/cmd.go

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ package server
33
import (
44
"context"
55
"fmt"
6-
"time"
76

87
"github.com/grafana/grafana-image-renderer/cmd/config"
98
"github.com/grafana/grafana-image-renderer/pkg/api"
10-
"github.com/grafana/grafana-image-renderer/pkg/chromium"
119
"github.com/grafana/grafana-image-renderer/pkg/metrics"
10+
"github.com/grafana/grafana-image-renderer/pkg/service"
1211
"github.com/urfave/cli/v3"
1312
)
1413

@@ -18,28 +17,39 @@ func NewCmd() *cli.Command {
1817
Usage: "Run the server part of the service.",
1918
Flags: []cli.Flag{
2019
&cli.StringFlag{
21-
Name: "addr",
22-
Usage: "The address to listen on for HTTP requests.",
23-
Value: ":8081",
24-
Sources: config.FromConfig("server.addr"),
20+
Name: "addr",
21+
Usage: "The address to listen on for HTTP requests.",
22+
Category: "Server",
23+
Value: ":8081",
24+
Sources: config.FromConfig("server.addr"),
2525
},
26+
&cli.StringFlag{
27+
Name: "auth-token",
28+
Usage: "The X-Auth-Token header value that must be sent to the service to permit requests.",
29+
Category: "Server",
30+
Value: "-",
31+
Sources: config.FromConfig("auth.token"),
32+
},
33+
2634
&cli.StringFlag{
2735
Name: "browser",
2836
Usage: "The path to the browser's binary. This is resolved against PATH.",
37+
Category: "Browser",
2938
TakesFile: true,
3039
Value: "chromium",
3140
Sources: config.FromConfig("browser.path"),
3241
},
3342
&cli.StringSliceFlag{
34-
Name: "browser-flags",
35-
Usage: "Flags to pass to the browser. These are syntaxed `<flag>` or `<flag>=<value>`. No -- should be passed in for the flag; these are implied.",
36-
Sources: config.FromConfig("browser.flags"),
43+
Name: "browser-flags",
44+
Usage: "Flags to pass to the browser. These are syntaxed `<flag>` or `<flag>=<value>`. No -- should be passed in for the flag; these are implied.",
45+
Category: "Browser",
46+
Sources: config.FromConfig("browser.flags"),
3747
},
38-
&cli.StringFlag{
39-
Name: "auth-token",
40-
Usage: "The X-Auth-Token header value that must be sent to the service to permit requests.",
41-
Value: "-",
42-
Sources: config.FromConfig("auth.token"),
48+
&cli.BoolFlag{
49+
Name: "browser-gpu",
50+
Usage: "Enable GPU support in the browser.",
51+
Category: "Browser",
52+
Sources: config.FromConfig("browser.gpu"),
4353
},
4454
},
4555
Action: run,
@@ -48,23 +58,11 @@ func NewCmd() *cli.Command {
4858

4959
func run(ctx context.Context, c *cli.Command) error {
5060
metrics := metrics.NewRegistry()
51-
browser, err := chromium.NewBrowser(c.String("browser"), c.StringSlice("browser-flags"), chromium.RenderingOptions{
52-
Width: 1000,
53-
Height: 500,
54-
TimeZone: "Etc/UTC",
55-
PaperSize: chromium.PaperA4,
56-
PrintBackground: true,
57-
Timeout: time.Second * 30,
58-
FullHeight: false,
59-
Landscape: true,
60-
Format: chromium.RenderingFormatPDF,
61-
DeviceScaleFactor: 2,
62-
TimeBetweenScrolls: time.Millisecond * 50,
63-
})
64-
if err != nil {
65-
return fmt.Errorf("failed to create browser: %w", err)
66-
}
67-
handler, err := api.NewHandler(metrics, browser, api.AuthToken(c.String("auth-token")))
61+
browser := service.NewBrowserService(c.String("browser"), c.StringSlice("browser-flags"),
62+
service.WithViewport(1000, 500),
63+
service.WithGPU(c.Bool("browser-gpu")))
64+
versions := service.NewVersionService()
65+
handler, err := api.NewHandler(metrics, browser, api.AuthToken(c.String("auth-token")), versions)
6866
if err != nil {
6967
return fmt.Errorf("failed to create API handler: %w", err)
7068
}

devenv/docker/enterprise-host/docker-compose.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ services:
66
GF_RENDERING_SERVER_URL: http://localhost:8081/render
77
GF_RENDERING_CALLBACK_URL: http://localhost:3000/
88
GF_LOG_FILTERS: rendering:debug
9+
GF_FEATURE_TOGGLES_ENABLE: sharingDashboardImage
10+
volumes:
11+
- grafana-data:/var/lib/grafana
912

1013
prometheus:
1114
image: prom/prometheus
1215
network_mode: host
16+
17+
volumes:
18+
grafana-data:

go.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,5 @@ EXPOSE 8081
7070

7171
ENTRYPOINT ["tini", "--", "/usr/bin/grafana-image-renderer"]
7272
CMD ["server"]
73-
HEALTHCHECK --interval=10s --retries=3 --timeout=3s \
73+
HEALTHCHECK --interval=10s --retries=3 --timeout=3s --start-interval=250ms --start-period=30s \
7474
CMD ["/usr/bin/grafana-image-renderer", "healthcheck"]

go.mod

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,85 @@ require (
66
github.com/Masterminds/semver/v3 v3.4.0
77
github.com/chromedp/cdproto v0.0.0-20250724212937-08a3db8b4327
88
github.com/chromedp/chromedp v0.14.1
9+
github.com/docker/docker v28.3.3+incompatible
10+
github.com/docker/go-connections v0.6.0
11+
github.com/go-jose/go-jose/v4 v4.1.2
912
github.com/google/uuid v1.6.0
1013
github.com/prometheus/client_golang v1.23.0
11-
github.com/stretchr/testify v1.10.0
14+
github.com/stretchr/testify v1.11.1
15+
github.com/testcontainers/testcontainers-go v0.38.0
16+
github.com/unidoc/unipdf/v4 v4.2.0
1217
github.com/urfave/cli-altsrc/v3 v3.0.1
1318
github.com/urfave/cli/v3 v3.4.1
1419
golang.org/x/sync v0.16.0
1520
gopkg.in/yaml.v3 v3.0.1
1621
)
1722

1823
require (
24+
dario.cat/mergo v1.0.2 // indirect
25+
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
26+
github.com/Microsoft/go-winio v0.6.2 // indirect
1927
github.com/beorn7/perks v1.0.1 // indirect
28+
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
2029
github.com/cespare/xxhash/v2 v2.3.0 // indirect
2130
github.com/chromedp/sysutil v1.1.0 // indirect
31+
github.com/containerd/errdefs v1.0.0 // indirect
32+
github.com/containerd/errdefs/pkg v0.3.0 // indirect
33+
github.com/containerd/log v0.1.0 // indirect
34+
github.com/containerd/platforms v0.2.1 // indirect
35+
github.com/cpuguy83/dockercfg v0.3.2 // indirect
2236
github.com/davecgh/go-spew v1.1.1 // indirect
37+
github.com/distribution/reference v0.6.0 // indirect
38+
github.com/docker/go-units v0.5.0 // indirect
39+
github.com/ebitengine/purego v0.8.4 // indirect
40+
github.com/felixge/httpsnoop v1.0.4 // indirect
2341
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 // indirect
42+
github.com/go-logr/logr v1.4.3 // indirect
43+
github.com/go-logr/stdr v1.2.2 // indirect
44+
github.com/go-ole/go-ole v1.3.0 // indirect
2445
github.com/gobwas/httphead v0.1.0 // indirect
2546
github.com/gobwas/pool v0.2.1 // indirect
2647
github.com/gobwas/ws v1.4.0 // indirect
27-
github.com/kr/text v0.2.0 // indirect
48+
github.com/gogo/protobuf v1.3.2 // indirect
49+
github.com/klauspost/compress v1.18.0 // indirect
50+
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 // indirect
51+
github.com/magiconair/properties v1.8.10 // indirect
52+
github.com/moby/docker-image-spec v1.3.1 // indirect
53+
github.com/moby/go-archive v0.1.0 // indirect
54+
github.com/moby/patternmatcher v0.6.0 // indirect
55+
github.com/moby/sys/sequential v0.6.0 // indirect
56+
github.com/moby/sys/user v0.4.0 // indirect
57+
github.com/moby/sys/userns v0.1.0 // indirect
58+
github.com/moby/term v0.5.2 // indirect
59+
github.com/morikuni/aec v1.0.0 // indirect
2860
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
61+
github.com/opencontainers/go-digest v1.0.0 // indirect
62+
github.com/opencontainers/image-spec v1.1.1 // indirect
63+
github.com/pkg/errors v0.9.1 // indirect
2964
github.com/pmezard/go-difflib v1.0.0 // indirect
65+
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
3066
github.com/prometheus/client_model v0.6.2 // indirect
3167
github.com/prometheus/common v0.65.0 // indirect
3268
github.com/prometheus/procfs v0.17.0 // indirect
69+
github.com/shirou/gopsutil/v4 v4.25.7 // indirect
70+
github.com/sirupsen/logrus v1.9.3 // indirect
71+
github.com/tklauser/go-sysconf v0.3.15 // indirect
72+
github.com/tklauser/numcpus v0.10.0 // indirect
73+
github.com/yusufpapurcu/wmi v1.2.4 // indirect
74+
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
75+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
76+
go.opentelemetry.io/otel v1.37.0 // indirect
77+
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
78+
go.opentelemetry.io/otel/metric v1.37.0 // indirect
79+
go.opentelemetry.io/otel/trace v1.37.0 // indirect
80+
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
81+
golang.org/x/crypto v0.41.0 // indirect
82+
golang.org/x/image v0.30.0 // indirect
3383
golang.org/x/mod v0.27.0 // indirect
3484
golang.org/x/sys v0.35.0 // indirect
85+
golang.org/x/text v0.28.0 // indirect
3586
golang.org/x/tools v0.36.0 // indirect
87+
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
3688
google.golang.org/protobuf v1.36.7 // indirect
3789
)
3890

0 commit comments

Comments
 (0)