Skip to content

Commit 340315b

Browse files
authored
feat: introduce print-config command (#886)
1 parent cdbdefd commit 340315b

File tree

8 files changed

+142
-30
lines changed

8 files changed

+142
-30
lines changed

cmd/printconfig/cmd.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package printconfig
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/davecgh/go-spew/spew"
8+
"github.com/grafana/grafana-image-renderer/cmd/server"
9+
"github.com/urfave/cli/v3"
10+
)
11+
12+
func NewCmd() *cli.Command {
13+
return &cli.Command{
14+
Name: "print-config",
15+
Usage: "Parse and print the current configuration. Replace 'server' with 'print-config' in the command line.",
16+
Flags: server.NewCmd().Flags,
17+
Action: func(ctx context.Context, c *cli.Command) error {
18+
cfg, err := server.ParseConfig(c)
19+
if err != nil {
20+
return fmt.Errorf("failed to parse config: %w", err)
21+
}
22+
spew.Dump(cfg)
23+
return nil
24+
},
25+
}
26+
}

cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"log/slog"
77

88
"github.com/grafana/grafana-image-renderer/cmd/healthcheck"
9+
"github.com/grafana/grafana-image-renderer/cmd/printconfig"
910
"github.com/grafana/grafana-image-renderer/cmd/sandbox"
1011
"github.com/grafana/grafana-image-renderer/cmd/server"
1112
"github.com/grafana/grafana-image-renderer/pkg/config"
@@ -41,6 +42,7 @@ func NewRootCmd() *cli.Command {
4142
healthcheck.NewCmd(),
4243
server.NewCmd(),
4344
sandbox.NewCmd(),
45+
printconfig.NewCmd(),
4446
},
4547
}
4648
}

cmd/server/cmd.go

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,33 +26,53 @@ func NewCmd() *cli.Command {
2626
}
2727
}
2828

29-
func run(ctx context.Context, c *cli.Command) error {
30-
_, err := maxprocs.Set(
31-
// We use maxprocs over automaxprocs because we need a new minimum value.
32-
// 2 is the absolute minimum we can handle, because we use multiple goroutines many places for timeouts.
33-
maxprocs.Min(2),
34-
maxprocs.Logger(maxProcsLog))
35-
if err != nil {
36-
slog.Info("failed to set GOMAXPROCS", "err", err)
37-
}
29+
type Cfg struct {
30+
server config.ServerConfig
31+
browser config.BrowserConfig
32+
tracing config.TracingConfig
33+
rateLimit config.RateLimitConfig
34+
}
3835

36+
func ParseConfig(c *cli.Command) (*Cfg, error) {
3937
serverConfig, err := config.ServerConfigFromCommand(c)
4038
if err != nil {
41-
return fmt.Errorf("failed to parse server config: %w", err)
39+
return nil, fmt.Errorf("failed to parse server config: %w", err)
4240
}
4341
browserConfig, err := config.BrowserConfigFromCommand(c)
4442
if err != nil {
45-
return fmt.Errorf("failed to parse browser config: %w", err)
43+
return nil, fmt.Errorf("failed to parse browser config: %w", err)
4644
}
4745
tracingConfig, err := config.TracingConfigFromCommand(c)
4846
if err != nil {
49-
return fmt.Errorf("failed to parse tracing config: %w", err)
47+
return nil, fmt.Errorf("failed to parse tracing config: %w", err)
5048
}
5149
rateLimitConfig, err := config.RateLimitConfigFromCommand(c)
5250
if err != nil {
53-
return fmt.Errorf("failed to parse process tracker config: %w", err)
51+
return nil, fmt.Errorf("failed to parse process tracker config: %w", err)
52+
}
53+
return &Cfg{
54+
server: serverConfig,
55+
browser: browserConfig,
56+
tracing: tracingConfig,
57+
rateLimit: rateLimitConfig,
58+
}, nil
59+
}
60+
61+
func run(ctx context.Context, c *cli.Command) error {
62+
_, err := maxprocs.Set(
63+
// We use maxprocs over automaxprocs because we need a new minimum value.
64+
// 2 is the absolute minimum we can handle, because we use multiple goroutines many places for timeouts.
65+
maxprocs.Min(2),
66+
maxprocs.Logger(maxProcsLog))
67+
if err != nil {
68+
slog.Info("failed to set GOMAXPROCS", "err", err)
69+
}
70+
71+
cfg, err := ParseConfig(c)
72+
if err != nil {
73+
return fmt.Errorf("failed to parse config: %w", err)
5474
}
55-
tracerProvider, err := traces.NewTracerProvider(ctx, tracingConfig)
75+
tracerProvider, err := traces.NewTracerProvider(ctx, cfg.tracing)
5676
if err != nil {
5777
return fmt.Errorf("failed to set up tracer: %w", err)
5878
}
@@ -62,15 +82,15 @@ func run(ctx context.Context, c *cli.Command) error {
6282
otel.SetTracerProvider(tracerProvider)
6383
otel.SetTextMapPropagator(propagation.TraceContext{})
6484
}
65-
processStatService := service.NewProcessStatService(rateLimitConfig)
66-
browser := service.NewBrowserService(browserConfig, processStatService)
85+
processStatService := service.NewProcessStatService(cfg.rateLimit)
86+
browser := service.NewBrowserService(cfg.browser, processStatService)
6787
versions := service.NewVersionService()
6888
metrics := metrics.NewRegistry()
69-
handler, err := api.NewHandler(metrics, serverConfig, rateLimitConfig, processStatService, browser, versions)
89+
handler, err := api.NewHandler(metrics, cfg.server, cfg.rateLimit, processStatService, browser, versions)
7090
if err != nil {
7191
return fmt.Errorf("failed to create API handler: %w", err)
7292
}
73-
return api.ListenAndServe(ctx, serverConfig, handler)
93+
return api.ListenAndServe(ctx, cfg.server, handler)
7494
}
7595

7696
func maxProcsLog(format string, args ...any) {

docs/sources/flags.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,25 @@ This is a verbatim copy of the output of the `grafana-image-renderer server --he
147147
--tracing.trusted-certificate=<string> [${TRACING_TRUSTED_CERTIFICATE}]
148148
A path to a PEM-encoded certificate to use as a trusted root when connecting to the tracing endpoint over gRPC or HTTPS. [config: tracing.trusted_certificate]
149149
```
150+
151+
## Debug the configuration
152+
153+
{{< admonition type="note" >}}
154+
The command recommended here prints secrets and sensitive information in
155+
plain text. Be careful when sharing the output or storing it in logs.
156+
{{< /admonition >}}
157+
158+
When the service doesn't appear to be working as expected, it can be because the
159+
configuration is invalid. If the flags are invalid, the service won't start, but
160+
the configuration files may contain unknown keys and thus silently ignore them.
161+
162+
To debug that the configuration is valid and what you expect, you can get a full
163+
dump of the Go structures representing the configuration by running the
164+
`print-config` command. For example:
165+
166+
```
167+
docker run --rm -v ./config.json:/home/nonroot/config.json grafana/grafana-image-renderer:latest print-config
168+
```
169+
170+
The command takes the exact same flags and configuration files as the `server`
171+
command does.

docs/sources/troubleshooting.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,3 +381,25 @@ To identify whether this applies to you, check whether the `newPDFRendering`
381381
feature flag has been explicitly set to `false` in Grafana's configuration.
382382

383383
To solve this problem, remove the feature toggle override.
384+
385+
## Debug the configuration
386+
387+
{{< admonition type="note" >}}
388+
The command recommended here prints secrets and sensitive information in
389+
plain text. Be careful when sharing the output or storing it in logs.
390+
{{< /admonition >}}
391+
392+
When the service doesn't appear to be working as expected, it can be because the
393+
configuration is invalid. If the flags are invalid, the service won't start, but
394+
the configuration files may contain unknown keys and thus silently ignore them.
395+
396+
To debug that the configuration is valid and what you expect, you can get a full
397+
dump of the Go structures representing the configuration by running the
398+
`print-config` command. For example:
399+
400+
```
401+
docker run --rm -v ./config.json:/home/nonroot/config.json grafana/grafana-image-renderer:latest print-config
402+
```
403+
404+
The command takes the exact same flags and configuration files as the `server`
405+
command does.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.25.3
55
require (
66
github.com/Masterminds/semver/v3 v3.4.0
77
github.com/chromedp/cdproto v0.0.0-20250803210736-d308e07a266d
8+
github.com/davecgh/go-spew v1.1.1
89
github.com/docker/docker v28.3.3+incompatible
910
github.com/docker/go-connections v0.6.0
1011
github.com/gen2brain/go-fitz v1.24.15
@@ -45,7 +46,6 @@ require (
4546
github.com/containerd/log v0.1.0 // indirect
4647
github.com/containerd/platforms v0.2.1 // indirect
4748
github.com/cpuguy83/dockercfg v0.3.2 // indirect
48-
github.com/davecgh/go-spew v1.1.1 // indirect
4949
github.com/distribution/reference v0.6.0 // indirect
5050
github.com/docker/go-units v0.5.0 // indirect
5151
github.com/ebitengine/purego v0.9.0 // indirect

scripts/help-documentation/help.md.tmpl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,25 @@ This is a verbatim copy of the output of the `grafana-image-renderer server --he
5050
```
5151
{{.Help}}
5252
```
53+
54+
## Debug the configuration
55+
56+
{{"{{< admonition type=\"note\" >}}"}}
57+
The command recommended here prints secrets and sensitive information in
58+
plain text. Be careful when sharing the output or storing it in logs.
59+
{{"{{< /admonition >}}"}}
60+
61+
When the service doesn't appear to be working as expected, it can be because the
62+
configuration is invalid. If the flags are invalid, the service won't start, but
63+
the configuration files may contain unknown keys and thus silently ignore them.
64+
65+
To debug that the configuration is valid and what you expect, you can get a full
66+
dump of the Go structures representing the configuration by running the
67+
`print-config` command. For example:
68+
69+
```
70+
docker run --rm -v ./config.json:/home/nonroot/config.json grafana/grafana-image-renderer:latest print-config
71+
```
72+
73+
The command takes the exact same flags and configuration files as the `server`
74+
command does.

scripts/help-documentation/main.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
_ "embed"
77
"fmt"
88
"io"
9-
"log/slog"
109
"maps"
1110
"os"
1211
"path/filepath"
@@ -27,7 +26,7 @@ type templateData struct {
2726

2827
func main() {
2928
if err := run(); err != nil {
30-
slog.Error("failed to run", "err", err)
29+
fmt.Println("application error:", err)
3130
os.Exit(1)
3231
}
3332
}
@@ -60,33 +59,32 @@ func run() error {
6059
return fmt.Errorf("failed to execute template: %w", err)
6160
}
6261

63-
gitRoot := findGitRoot()
64-
if gitRoot == "" {
65-
return fmt.Errorf("failed to find git root")
62+
gitRoot, err := findGitRoot()
63+
if err != nil {
64+
return fmt.Errorf("failed to find git root: %w", err)
6665
}
6766

6867
helpFile := filepath.Join(gitRoot, "docs", "sources", "flags.md")
6968
if err := os.WriteFile(helpFile, buffer.Bytes(), 0644); err != nil {
7069
return fmt.Errorf("failed to write help file to %q: %w", helpFile, err)
7170
}
7271

73-
slog.Info("wrote help", "file", helpFile)
72+
fmt.Println("Help documentation generated at", helpFile)
7473
return nil
7574
}
7675

77-
func findGitRoot() string {
76+
func findGitRoot() (string, error) {
7877
dir, err := os.Getwd()
7978
if err != nil {
80-
slog.Error("failed to get current working directory", "err", err)
81-
os.Exit(1)
79+
return "", fmt.Errorf("failed to get working directory: %w", err)
8280
}
8381
for {
8482
if _, err := os.Stat(filepath.Join(dir, ".git")); err == nil {
85-
return dir
83+
return dir, nil
8684
}
8785
dir = filepath.Dir(dir)
8886
if dir == "/" {
89-
return ""
87+
return "", fmt.Errorf("git root not found")
9088
}
9189
}
9290
}

0 commit comments

Comments
 (0)