Skip to content

fix: query errors with newer Grafana API#254

Open
llamafilm wants to merge 2 commits intografana:mainfrom
llamafilm:query-errors
Open

fix: query errors with newer Grafana API#254
llamafilm wants to merge 2 commits intografana:mainfrom
llamafilm:query-errors

Conversation

@llamafilm
Copy link
Copy Markdown

@llamafilm llamafilm commented Apr 14, 2026

Closes #253

Bug Fixes

Extend IsDataSourceQueryRequest to match both the legacy /api/ds/query? path and the newer
/apis/query.grafana.app/<version>/namespaces/<ns>/query path used by Grafana Cloud

Tests

Add test cases for query API path matching (correct host, with port, wrong host, non-query API path)

Details

When using login-method=apikey, the kiosk intercepts browser requests via the Chrome DevTools Protocol fetch domain
to inject Authorization and Content-Type headers. The Authorization: Bearer header was added to all requests
matching the target host, but the Content-Type: application/json header was only added when the request URL contained /api/ds/query? — the legacy Grafana datasource query path.

Grafana Cloud now routes datasource queries through a Kubernetes-style API:
/apis/query.grafana.app/v0alpha1/namespaces/stacks-/query?ds_type=prometheus&requestId=...

Without the Content-Type header, the server cannot parse the POST body and returns a 400 Bad Request.

@cla-assistant
Copy link
Copy Markdown

cla-assistant bot commented Apr 14, 2026

CLA assistant check
All committers have signed the CLA.

@briangann briangann self-requested a review April 14, 2026 05:30
@briangann briangann added the bug Something isn't working label Apr 14, 2026
@grafana grafana deleted a comment from cla-assistant bot Apr 15, 2026
@briangann
Copy link
Copy Markdown
Collaborator

briangann commented Apr 15, 2026

Nice fix! One logic issue to flag:

Redundant second condition in new path match

pkg/kiosk/apikey_login.go:33 (in the PR diff):

return strings.Contains(rest, "/apis/query.grafana.app/") && strings.Contains(rest, "/query")

The second strings.Contains(rest, "/query") is always true when the first passes, because the literal string /apis/query.grafana.app/ already contains the substring /query. This means any path under query.grafana.app matches — not just the /query endpoint.

For example, this would incorrectly return true:

https://myorg.grafana.net/apis/query.grafana.app/v0alpha1/namespaces/stacks-123/something-else

Suggested fix — replace pkg/kiosk/apikey_login.go:33 with:

	path := strings.SplitN(rest, "?", 2)[0]

	return strings.Contains(rest, "/apis/query.grafana.app/") && strings.HasSuffix(path, "/query")

Strip the query string first, then use HasSuffix to verify the path actually ends with /query.

Suggested test — add after pkg/kiosk/apikey_login_test.go:90 (after the "not a query endpoint" block):

Convey("When request is under query.grafana.app but not a /query endpoint", func() {
    result := IsDataSourceQueryRequest(
        "https://myorg.grafana.net/apis/query.grafana.app/v0alpha1/namespaces/stacks-123/something-else",
        "https", "myorg.grafana.net",
    )
    So(result, ShouldBeFalse)
})

This test exposes the bug — without the HasSuffix fix it returns true.

Tested locally — all 11 assertions pass with these changes. Low severity since false positives are unlikely in practice, but easy to tighten up.

Copy link
Copy Markdown
Collaborator

@briangann briangann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR looks great, see the comments above to improve the code a little bit. If you don't want to make those changes right now I can open an issue and get it fixed up later.

@llamafilm
Copy link
Copy Markdown
Author

Nice catch. FIxed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP 400 error reading query

2 participants