Prometheus: Fix X-Id-Token and X-ID-Token sent to Prometheus in query requests (#60342)
* Prometheus: Use Set rather than map assignment in sdkHeaderToHttpHeader Fixes #59940 * Prometheus: Add TestPrometheusCanonicalHeaders
This commit is contained in:
@@ -187,7 +187,7 @@ func (s *QueryData) trace(ctx context.Context, q *models.Query) (context.Context
|
||||
func sdkHeaderToHttpHeader(headers map[string]string) http.Header {
|
||||
httpHeader := make(http.Header, len(headers))
|
||||
for key, val := range headers {
|
||||
httpHeader[key] = []string{val}
|
||||
httpHeader.Set(key, val)
|
||||
}
|
||||
return httpHeader
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/tsdb/prometheus/client"
|
||||
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
||||
p "github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/httpclient"
|
||||
@@ -342,15 +343,36 @@ func TestPrometheus_parseTimeSeriesResponse(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPrometheusCanonicalHeaders(t *testing.T) {
|
||||
// Ensure headers are always canonicalized for all outgoing requests
|
||||
b, err := json.Marshal(models.QueryModel{})
|
||||
require.NoError(t, err)
|
||||
query := backend.DataQuery{JSON: b}
|
||||
tctx, err := setup(true)
|
||||
require.NoError(t, err)
|
||||
const idToken = "abc"
|
||||
_, err = executeWithHeaders(tctx, query, queryResult{}, map[string]string{
|
||||
"X-Id-Token": idToken,
|
||||
"X-ID-Token": idToken,
|
||||
"X-Other": "thing",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, tctx.httpProvider.req.Header)
|
||||
// Check the request that hit the fake prometheus server to ensure headers are valid
|
||||
assert.Equal(t, []string{idToken}, tctx.httpProvider.req.Header["X-Id-Token"])
|
||||
assert.Empty(t, tctx.httpProvider.req.Header["X-ID-Token"]) //nolint:staticcheck
|
||||
assert.Equal(t, []string{"thing"}, tctx.httpProvider.req.Header["X-Other"])
|
||||
}
|
||||
|
||||
type queryResult struct {
|
||||
Type p.ValueType `json:"resultType"`
|
||||
Result interface{} `json:"result"`
|
||||
}
|
||||
|
||||
func execute(tctx *testContext, query backend.DataQuery, qr interface{}) (data.Frames, error) {
|
||||
func executeWithHeaders(tctx *testContext, query backend.DataQuery, qr interface{}, headers map[string]string) (data.Frames, error) {
|
||||
req := backend.QueryDataRequest{
|
||||
Queries: []backend.DataQuery{query},
|
||||
Headers: map[string]string{},
|
||||
Headers: headers,
|
||||
}
|
||||
|
||||
promRes, err := toAPIResponse(qr)
|
||||
@@ -367,6 +389,10 @@ func execute(tctx *testContext, query backend.DataQuery, qr interface{}) (data.F
|
||||
return res.Responses[req.Queries[0].RefID].Frames, nil
|
||||
}
|
||||
|
||||
func execute(tctx *testContext, query backend.DataQuery, qr interface{}) (data.Frames, error) {
|
||||
return executeWithHeaders(tctx, query, qr, map[string]string{})
|
||||
}
|
||||
|
||||
type apiResponse struct {
|
||||
Status string `json:"status"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
@@ -447,6 +473,7 @@ func (f *fakeFeatureToggles) IsEnabled(feature string) bool {
|
||||
type fakeHttpClientProvider struct {
|
||||
httpclient.Provider
|
||||
opts sdkhttpclient.Options
|
||||
req *http.Request
|
||||
res *http.Response
|
||||
}
|
||||
|
||||
@@ -470,5 +497,6 @@ func (p *fakeHttpClientProvider) setResponse(res *http.Response) {
|
||||
}
|
||||
|
||||
func (p *fakeHttpClientProvider) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
p.req = req
|
||||
return p.res, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user