Skip to content

Commit ca39b4c

Browse files
authored
query-frontend: add option to preserve specific request headers
To make Cortex query-frontend more flexible when used with other Prometheus engines. Option is named -frontend.forward-headers-list. Signed-off-by: Manish Kumar Gupta <[email protected]>
1 parent 425f108 commit ca39b4c

11 files changed

+500
-82
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* [FEATURE] Query Frontend: Add `cortex_query_fetched_series_total` and `cortex_query_fetched_chunks_bytes_total` per-user counters to expose the number of series and bytes fetched as part of queries. These metrics can be enabled with the `-frontend.query-stats-enabled` flag (or its respective YAML config option `query_stats_enabled`). #4343
2727
* [FEATURE] AlertManager: Add support for SNS Receiver. #4382
2828
* [FEATURE] Distributor: Add label `status` to metric `cortex_distributor_ingester_append_failures_total` #4442
29+
* [ENHANCEMENT] Query Frontend: Add setting `-frontend.forward-headers-list` in frontend to configure the set of headers from the requests to be forwarded to downstream requests. #4486
2930
* [FEATURE] Queries: Added `present_over_time` PromQL function, also some TSDB optimisations. #4505
3031
* [ENHANCEMENT] Add timeout for waiting on compactor to become ACTIVE in the ring. #4262
3132
* [ENHANCEMENT] Reduce memory used by streaming queries, particularly in ruler. #4341

docs/configuration/arguments.md

+4
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ The ingester query API was improved over time, but defaults to the old behaviour
116116

117117
If set to true, will cause the querier to cache query results. The cache will be used to answer future, overlapping queries. The query frontend calculates extra queries required to fill gaps in the cache.
118118

119+
- `-frontend.forward-headers-list`
120+
121+
Request headers forwarded by query frontend to downstream queriers. Multiple headers may be specified. Defaults to empty.
122+
119123
- `-frontend.max-cache-freshness`
120124

121125
When caching query results, it is desirable to prevent the caching of very recent results that might still be in flux. Use this parameter to configure the age of results that should be excluded.

docs/configuration/config-file-reference.md

+4
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,10 @@ results_cache:
11661166
# query ASTs. This feature is supported only by the chunks storage engine.
11671167
# CLI flag: -querier.parallelise-shardable-queries
11681168
[parallelise_shardable_queries: <boolean> | default = false]
1169+
1170+
# List of headers forwarded by the query Frontend to downstream querier.
1171+
# CLI flag: -frontend.forward-headers-list
1172+
[forward_headers_list: <list of string> | default = []]
11691173
```
11701174

11711175
### `ruler_config`

docs/configuration/prometheus-frontend.md

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ query_range:
2828
align_queries_with_step: true
2929
cache_results: true
3030

31+
# list of request headers forwarded by query frontend to downstream queriers.
32+
forward_headers_list:
33+
- Authorization
34+
3135
results_cache:
3236
cache:
3337

pkg/querier/queryrange/query_range.go

+21-3
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ var (
5252
type Codec interface {
5353
Merger
5454
// DecodeRequest decodes a Request from an http request.
55-
DecodeRequest(context.Context, *http.Request) (Request, error)
55+
DecodeRequest(_ context.Context, request *http.Request, forwardHeaders []string) (Request, error)
5656
// DecodeResponse decodes a Response from an http response.
5757
// The original request is also passed as a parameter this is useful for implementation that needs the request
5858
// to merge result or build the result correctly.
@@ -187,7 +187,7 @@ func (prometheusCodec) MergeResponse(responses ...Response) (Response, error) {
187187
return &response, nil
188188
}
189189

190-
func (prometheusCodec) DecodeRequest(_ context.Context, r *http.Request) (Request, error) {
190+
func (prometheusCodec) DecodeRequest(_ context.Context, r *http.Request, forwardHeaders []string) (Request, error) {
191191
var result PrometheusRequest
192192
var err error
193193
result.Start, err = util.ParseTime(r.FormValue("start"))
@@ -222,6 +222,16 @@ func (prometheusCodec) DecodeRequest(_ context.Context, r *http.Request) (Reques
222222
result.Query = r.FormValue("query")
223223
result.Path = r.URL.Path
224224

225+
// Include the specified headers from http request in prometheusRequest.
226+
for _, header := range forwardHeaders {
227+
for h, hv := range r.Header {
228+
if strings.EqualFold(h, header) {
229+
result.Headers = append(result.Headers, &PrometheusRequestHeader{Name: h, Values: hv})
230+
break
231+
}
232+
}
233+
}
234+
225235
for _, value := range r.Header.Values(cacheControlHeader) {
226236
if strings.Contains(value, noStoreValue) {
227237
result.CachingOptions.Disabled = true
@@ -247,12 +257,20 @@ func (prometheusCodec) EncodeRequest(ctx context.Context, r Request) (*http.Requ
247257
Path: promReq.Path,
248258
RawQuery: params.Encode(),
249259
}
260+
var h = http.Header{}
261+
262+
for _, hv := range promReq.Headers {
263+
for _, v := range hv.Values {
264+
h.Add(hv.Name, v)
265+
}
266+
}
267+
250268
req := &http.Request{
251269
Method: "GET",
252270
RequestURI: u.String(), // This is what the httpgrpc code looks at.
253271
URL: u,
254272
Body: http.NoBody,
255-
Header: http.Header{},
273+
Header: h,
256274
}
257275

258276
return req.WithContext(ctx), nil

pkg/querier/queryrange/query_range_test.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@ import (
1818
)
1919

2020
func TestRequest(t *testing.T) {
21+
// Create a Copy parsedRequest to assign the expected headers to the request without affecting other tests using the global.
22+
// The test below adds a Test-Header header to the request and expects it back once the encode/decode of request is done via PrometheusCodec
23+
parsedRequestWithHeaders := *parsedRequest
24+
parsedRequestWithHeaders.Headers = reqHeaders
2125
for i, tc := range []struct {
2226
url string
2327
expected Request
2428
expectedErr error
2529
}{
2630
{
2731
url: query,
28-
expected: parsedRequest,
32+
expected: &parsedRequestWithHeaders,
2933
},
3034
{
3135
url: "api/v1/query_range?start=foo",
@@ -55,11 +59,14 @@ func TestRequest(t *testing.T) {
5559
t.Run(strconv.Itoa(i), func(t *testing.T) {
5660
r, err := http.NewRequest("GET", tc.url, nil)
5761
require.NoError(t, err)
62+
r.Header.Add("Test-Header", "test")
5863

5964
ctx := user.InjectOrgID(context.Background(), "1")
60-
r = r.WithContext(ctx)
6165

62-
req, err := PrometheusCodec.DecodeRequest(ctx, r)
66+
// Get a deep copy of the request with Context changed to ctx
67+
r = r.Clone(ctx)
68+
69+
req, err := PrometheusCodec.DecodeRequest(ctx, r, []string{"Test-Header"})
6370
if err != nil {
6471
require.EqualValues(t, tc.expectedErr, err)
6572
return

0 commit comments

Comments
 (0)