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:
Giuseppe Guerra
2022-12-15 17:18:38 +01:00
committed by GitHub
parent 6637333748
commit 92c01e416e
2 changed files with 31 additions and 3 deletions
+1 -1
View File
@@ -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
}
+30 -2
View File
@@ -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
}