@@ -33,33 +33,46 @@ func urlAndAPIKeyFromEnv() (string, string) {
33
33
}
34
34
35
35
func urlAndAPIKeyFromHeaders(req *http.Request) (string, string) {
36
-
u := req.Header.Get(grafanaURLHeader)
36
+
u := strings.TrimRight(req.Header.Get(grafanaURLHeader), "/")
37
37
apiKey := req.Header.Get(grafanaAPIKeyHeader)
38
38
return u, apiKey
39
39
}
40
40
41
-
type grafanaURLKey struct{}
42
-
type grafanaAPIKeyKey struct{}
43
-
type grafanaAccessTokenKey struct{}
41
+
// grafanaConfigKey is the context key for Grafana configuration.
42
+
type grafanaConfigKey struct{}
44
43
45
-
// grafanaDebugKey is the context key for the Grafana transport's debug flag.
46
-
type grafanaDebugKey struct{}
44
+
// GrafanaConfig represents the full configuration for Grafana clients.
45
+
type GrafanaConfig struct {
46
+
// Debug enables debug mode for the Grafana client.
47
+
Debug bool
47
48
48
-
// WithGrafanaDebug adds the Grafana debug flag to the context.
49
-
func WithGrafanaDebug(ctx context.Context, debug bool) context.Context {
50
-
if debug {
51
-
slog.Info("Grafana transport debug mode enabled")
52
-
}
53
-
return context.WithValue(ctx, grafanaDebugKey{}, debug)
49
+
// URL is the URL of the Grafana instance.
50
+
URL string
51
+
52
+
// APIKey is the API key or service account token for the Grafana instance.
53
+
// It may be empty if we are using on-behalf-of auth.
54
+
APIKey string
55
+
56
+
// AccessToken is the Grafana Cloud access policy token used for on-behalf-of auth in Grafana Cloud.
57
+
AccessToken string
58
+
// IDToken is an ID token identifying the user for the current request.
59
+
// It comes from the `X-Grafana-Id` header sent from Grafana to plugin backends.
60
+
// It is used for on-behalf-of auth in Grafana Cloud.
61
+
IDToken string
62
+
}
63
+
64
+
// WithGrafanaConfig adds Grafana configuration to the context.
65
+
func WithGrafanaConfig(ctx context.Context, config GrafanaConfig) context.Context {
66
+
return context.WithValue(ctx, grafanaConfigKey{}, config)
54
67
}
55
68
56
-
// GrafanaDebugFromContext extracts the Grafana debug flag from the context.
57
-
// If the flag is not set, it returns false.
58
-
func GrafanaDebugFromContext(ctx context.Context) bool {
59
-
if debug, ok := ctx.Value(grafanaDebugKey{}).(bool); ok {
60
-
return debug
69
+
// GrafanaConfigFromContext extracts Grafana configuration from the context.
70
+
// If no config is found, returns a zero-value GrafanaConfig.
71
+
func GrafanaConfigFromContext(ctx context.Context) GrafanaConfig {
72
+
if config, ok := ctx.Value(grafanaConfigKey{}).(GrafanaConfig); ok {
73
+
return config
61
74
}
62
-
return false
75
+
return GrafanaConfig{}
63
76
}
64
77
65
78
// ExtractGrafanaInfoFromEnv is a StdioContextFunc that extracts Grafana configuration
@@ -74,7 +87,13 @@ var ExtractGrafanaInfoFromEnv server.StdioContextFunc = func(ctx context.Context
74
87
panic(fmt.Errorf("invalid Grafana URL %s: %w", u, err))
75
88
}
76
89
slog.Info("Using Grafana configuration", "url", parsedURL.Redacted(), "api_key_set", apiKey != "")
77
-
return WithGrafanaURL(WithGrafanaAPIKey(ctx, apiKey), u)
90
+
91
+
// Get existing config or create a new one.
92
+
// This will respect the existing debug flag, if set.
93
+
config := GrafanaConfigFromContext(ctx)
94
+
config.URL = u
95
+
config.APIKey = apiKey
96
+
return WithGrafanaConfig(ctx, config)
78
97
}
79
98
80
99
// httpContextFunc is a function that can be used as a `server.HTTPContextFunc` or a
@@ -96,30 +115,29 @@ var ExtractGrafanaInfoFromHeaders httpContextFunc = func(ctx context.Context, re
96
115
if apiKey == "" {
97
116
apiKey = apiKeyEnv
98
117
}
99
-
return WithGrafanaURL(WithGrafanaAPIKey(ctx, apiKey), u)
100
-
}
101
118
102
-
// WithGrafanaURL adds the Grafana URL to the context.
103
-
func WithGrafanaURL(ctx context.Context, url string) context.Context {
104
-
return context.WithValue(ctx, grafanaURLKey{}, url)
105
-
}
106
-
107
-
// WithGrafanaAPIKey adds the Grafana API key to the context.
108
-
func WithGrafanaAPIKey(ctx context.Context, apiKey string) context.Context {
109
-
return context.WithValue(ctx, grafanaAPIKeyKey{}, apiKey)
119
+
// Get existing config or create a new one.
120
+
// This will respect the existing debug flag, if set.
121
+
config := GrafanaConfigFromContext(ctx)
122
+
config.URL = u
123
+
config.APIKey = apiKey
124
+
return WithGrafanaConfig(ctx, config)
110
125
}
111
126
112
127
// WithOnBehalfOfAuth adds the Grafana access token and user token to the
113
-
// context. These tokens are used for on-behalf-of auth in Grafana Cloud.
128
+
// Grafana config. These tokens are used for on-behalf-of auth in Grafana Cloud.
114
129
func WithOnBehalfOfAuth(ctx context.Context, accessToken, userToken string) (context.Context, error) {
115
130
if accessToken == "" || userToken == "" {
116
131
return nil, fmt.Errorf("neither accessToken nor userToken can be empty")
117
132
}
118
-
return context.WithValue(ctx, grafanaAccessTokenKey{}, []string{accessToken, userToken}), nil
133
+
cfg := GrafanaConfigFromContext(ctx)
134
+
cfg.AccessToken = accessToken
135
+
cfg.IDToken = userToken
136
+
return WithGrafanaConfig(ctx, cfg), nil
119
137
}
120
138
121
139
// MustWithOnBehalfOfAuth adds the access and user tokens to the context,
122
-
// panicing if either are empty.
140
+
// panicking if either are empty.
123
141
func MustWithOnBehalfOfAuth(ctx context.Context, accessToken, userToken string) context.Context {
124
142
ctx, err := WithOnBehalfOfAuth(ctx, accessToken, userToken)
125
143
if err != nil {
@@ -128,31 +146,6 @@ func MustWithOnBehalfOfAuth(ctx context.Context, accessToken, userToken string)
128
146
return ctx
129
147
}
130
148
131
-
// GrafanaURLFromContext extracts the Grafana URL from the context.
132
-
func GrafanaURLFromContext(ctx context.Context) string {
133
-
if u, ok := ctx.Value(grafanaURLKey{}).(string); ok {
134
-
return u
135
-
}
136
-
return defaultGrafanaURL
137
-
}
138
-
139
-
// GrafanaAPIKeyFromContext extracts the Grafana API key from the context.
140
-
func GrafanaAPIKeyFromContext(ctx context.Context) string {
141
-
if k, ok := ctx.Value(grafanaAPIKeyKey{}).(string); ok {
142
-
return k
143
-
}
144
-
return ""
145
-
}
146
-
147
-
// OnBehalfOfAuthFromContext extracts the Grafana access and user tokens from
148
-
// the context. These tokens are used for on-behalf-of auth in Grafana Cloud.
149
-
func OnBehalfOfAuthFromContext(ctx context.Context) (string, string) {
150
-
if k, ok := ctx.Value(grafanaAccessTokenKey{}).([]string); ok {
151
-
return k[0], k[1]
152
-
}
153
-
return "", ""
154
-
}
155
-
156
149
type grafanaClientKey struct{}
157
150
158
151
func makeBasePath(path string) string {
@@ -188,7 +181,8 @@ func NewGrafanaClient(ctx context.Context, grafanaURL, apiKey string) *client.Gr
188
181
cfg.APIKey = apiKey
189
182
}
190
183
191
-
cfg.Debug = GrafanaDebugFromContext(ctx)
184
+
config := GrafanaConfigFromContext(ctx)
185
+
cfg.Debug = config.Debug
192
186
193
187
slog.Debug("Creating Grafana client", "url", parsedURL.Redacted(), "api_key_set", apiKey != "")
194
188
return client.NewHTTPClientWithConfig(strfmt.Default, cfg)
@@ -258,6 +252,7 @@ var ExtractIncidentClientFromEnv server.StdioContextFunc = func(ctx context.Cont
258
252
}
259
253
slog.Debug("Creating Incident client", "url", parsedURL.Redacted(), "api_key_set", apiKey != "")
260
254
client := incident.NewClient(incidentURL, apiKey)
255
+
261
256
return context.WithValue(ctx, incidentClientKey{}, client)
262
257
}
263
258
@@ -275,6 +270,7 @@ var ExtractIncidentClientFromHeaders httpContextFunc = func(ctx context.Context,
275
270
}
276
271
incidentURL := fmt.Sprintf("%s/api/plugins/grafana-irm-app/resources/api/v1/", grafanaURL)
277
272
client := incident.NewClient(incidentURL, apiKey)
273
+
278
274
return context.WithValue(ctx, incidentClientKey{}, client)
279
275
}
280
276
@@ -322,10 +318,10 @@ func ComposeHTTPContextFuncs(funcs ...httpContextFunc) server.HTTPContextFunc {
322
318
323
319
// ComposedStdioContextFunc returns a StdioContextFunc that comprises all predefined StdioContextFuncs,
324
320
// as well as the Grafana debug flag.
325
-
func ComposedStdioContextFunc(debug bool) server.StdioContextFunc {
321
+
func ComposedStdioContextFunc(config GrafanaConfig) server.StdioContextFunc {
326
322
return ComposeStdioContextFuncs(
327
323
func(ctx context.Context) context.Context {
328
-
return WithGrafanaDebug(ctx, debug)
324
+
return WithGrafanaConfig(ctx, config)
329
325
},
330
326
ExtractGrafanaInfoFromEnv,
331
327
ExtractGrafanaClientFromEnv,
@@ -334,10 +330,10 @@ func ComposedStdioContextFunc(debug bool) server.StdioContextFunc {
334
330
}
335
331
336
332
// ComposedSSEContextFunc is a SSEContextFunc that comprises all predefined SSEContextFuncs.
337
-
func ComposedSSEContextFunc(debug bool) server.SSEContextFunc {
333
+
func ComposedSSEContextFunc(config GrafanaConfig) server.SSEContextFunc {
338
334
return ComposeSSEContextFuncs(
339
335
func(ctx context.Context, req *http.Request) context.Context {
340
-
return WithGrafanaDebug(ctx, debug)
336
+
return WithGrafanaConfig(ctx, config)
341
337
},
342
338
ExtractGrafanaInfoFromHeaders,
343
339
ExtractGrafanaClientFromHeaders,
@@ -346,10 +342,10 @@ func ComposedSSEContextFunc(debug bool) server.SSEContextFunc {
346
342
}
347
343
348
344
// ComposedHTTPContextFunc is a HTTPContextFunc that comprises all predefined HTTPContextFuncs.
349
-
func ComposedHTTPContextFunc(debug bool) server.HTTPContextFunc {
345
+
func ComposedHTTPContextFunc(config GrafanaConfig) server.HTTPContextFunc {
350
346
return ComposeHTTPContextFuncs(
351
347
func(ctx context.Context, req *http.Request) context.Context {
352
-
return WithGrafanaDebug(ctx, debug)
348
+
return WithGrafanaConfig(ctx, config)
353
349
},
354
350
ExtractGrafanaInfoFromHeaders,
355
351
ExtractGrafanaClientFromHeaders,
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4