Skip to content

Commit 9f77e36

Browse files
committed
refactor querier unit tests
Signed-off-by: Ahmed Hassan <[email protected]> Signed-off-by: Ahmed Hassan <[email protected]>
1 parent 3d35da3 commit 9f77e36

15 files changed

+881
-2508
lines changed

pkg/api/handlers.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ package api
33
import (
44
"context"
55
"encoding/json"
6-
thanos_api "github.com/thanos-io/thanos/pkg/api"
76
"html/template"
87
"net/http"
98
"path"
109
"sync"
1110

11+
thanos_api "github.com/thanos-io/thanos/pkg/api"
12+
1213
"github.com/go-kit/log"
1314
"github.com/go-kit/log/level"
1415
"github.com/gorilla/mux"
@@ -22,6 +23,7 @@ import (
2223
"github.com/prometheus/prometheus/config"
2324
"github.com/prometheus/prometheus/promql"
2425
"github.com/prometheus/prometheus/storage"
26+
"github.com/prometheus/prometheus/util/annotations"
2527
v1api "github.com/prometheus/prometheus/web/api/v1"
2628
"github.com/weaveworks/common/instrument"
2729
"github.com/weaveworks/common/middleware"
@@ -233,16 +235,6 @@ func NewQuerierHandler(
233235
false,
234236
)
235237

236-
queryapi := qapi.NewAPI(
237-
engine,
238-
querier.NewErrorTranslateSampleAndChunkQueryable(queryable), // Translate errors to errors expected by API.
239-
func(f http.HandlerFunc) http.HandlerFunc { return f },
240-
logger,
241-
false,
242-
regexp.MustCompile(".*"),
243-
nil,
244-
)
245-
246238
queryapi := qapi.NewAPI(
247239
engine,
248240
querier.NewErrorTranslateSampleAndChunkQueryable(queryable), // Translate errors to errors expected by API.
@@ -273,9 +265,9 @@ func NewQuerierHandler(
273265
legacyPromRouter := route.New().WithPrefix(path.Join(legacyPrefix, "/api/v1"))
274266
v1api.Register(legacyPromRouter)
275267

276-
wrap := func(f thanos_api.ApiFunc) http.HandlerFunc {
268+
wrap := func(f func(r *http.Request) (interface{}, *thanos_api.ApiError, annotations.Annotations, func())) http.HandlerFunc {
277269
hf := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
278-
data, warnings, err, finalizer := f(r)
270+
data, err, warnings, finalizer := f(r)
279271
if finalizer != nil {
280272
defer finalizer()
281273
}
@@ -285,7 +277,7 @@ func NewQuerierHandler(
285277
}
286278

287279
if data != nil {
288-
queryapi.Respond(w, data, warnings)
280+
queryapi.Respond(w, r, data, warnings, r.FormValue("query"))
289281
return
290282
}
291283
w.WriteHeader(http.StatusNoContent)

pkg/querier/handler/handler.go

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ package handler
33
import (
44
"context"
55
"fmt"
6+
"math"
7+
"net/http"
8+
"strconv"
9+
"strings"
10+
"time"
11+
612
"github.com/cortexproject/cortex/pkg/querier/tripperware/instantquery"
713
"github.com/go-kit/log"
814
"github.com/go-kit/log/level"
@@ -11,19 +17,15 @@ import (
1117
"github.com/pkg/errors"
1218
"github.com/prometheus/common/model"
1319
v1 "github.com/prometheus/prometheus/web/api/v1"
14-
"math"
15-
"net/http"
16-
"strconv"
17-
"strings"
18-
"time"
1920

2021
"github.com/cortexproject/cortex/pkg/cortexpb"
21-
github_com_cortexproject_cortex_pkg_cortexpb "github.com/cortexproject/cortex/pkg/cortexpb"
22+
cortex_pb "github.com/cortexproject/cortex/pkg/cortexpb"
2223
"github.com/cortexproject/cortex/pkg/querier/tripperware"
2324
"github.com/cortexproject/cortex/pkg/querier/tripperware/queryrange"
2425
"github.com/prometheus/prometheus/promql"
2526
"github.com/prometheus/prometheus/promql/parser"
2627
"github.com/prometheus/prometheus/storage"
28+
"github.com/prometheus/prometheus/util/annotations"
2729
"github.com/prometheus/prometheus/util/httputil"
2830
"github.com/prometheus/prometheus/util/stats"
2931
thanos_api "github.com/thanos-io/thanos/pkg/api"
@@ -67,15 +69,15 @@ type response struct {
6769

6870
type API struct {
6971
Queryable storage.SampleAndChunkQueryable
70-
QueryEngine v1.QueryEngine
72+
QueryEngine promql.QueryEngine
7173
Now func() time.Time
7274
Logger log.Logger
7375
StatsRenderer v1.StatsRenderer
7476
}
7577

7678
// NewAPI returns an initialized API type.
7779
func NewAPI(
78-
qe v1.QueryEngine,
80+
qe promql.QueryEngine,
7981
q storage.SampleAndChunkQueryable,
8082
logger log.Logger,
8183
statsRenderer v1.StatsRenderer,
@@ -101,13 +103,13 @@ type queryData struct {
101103
Stats stats.QueryStats `json:"stats,omitempty"`
102104
}
103105

104-
func invalidParamError(err error, parameter string) (data interface{}, warnings []error, error *thanos_api.ApiError, finalizer func()) {
105-
return nil, nil, &thanos_api.ApiError{
106+
func invalidParamError(err error, parameter string) (data interface{}, error *thanos_api.ApiError, warnings annotations.Annotations, finalizer func()) {
107+
return nil, &thanos_api.ApiError{
106108
thanos_api.ErrorBadData, errors.Wrapf(err, "invalid parameter %q", parameter),
107-
}, nil
109+
}, nil, nil
108110
}
109111

110-
func (api *API) Query(r *http.Request) (data interface{}, warnings []error, error *thanos_api.ApiError, finalizer func()) {
112+
func (api *API) Query(r *http.Request) (data interface{}, error *thanos_api.ApiError, warnings annotations.Annotations, finalizer func()) {
111113
ts, err := parseTimeParam(r, "time", api.Now())
112114
if err != nil {
113115
return invalidParamError(err, "time")
@@ -120,13 +122,13 @@ func (api *API) Query(r *http.Request) (data interface{}, warnings []error, erro
120122
return invalidParamError(err, "timeout")
121123
}
122124

123-
ctx, cancel = context.WithTimeout(ctx, timeout)
125+
ctx, cancel = context.WithDeadline(ctx, api.Now().Add(timeout))
124126
defer cancel()
125127
}
126128

127129
opts, err := extractQueryOpts(r)
128130
if err != nil {
129-
return nil, nil, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, nil
131+
return nil, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, nil, nil
130132
}
131133
qry, err := api.QueryEngine.NewInstantQuery(ctx, api.Queryable, opts, r.FormValue("query"), ts)
132134
if err != nil {
@@ -146,7 +148,7 @@ func (api *API) Query(r *http.Request) (data interface{}, warnings []error, erro
146148

147149
res := qry.Exec(ctx)
148150
if res.Err != nil {
149-
return nil, res.Warnings, returnAPIError(res.Err), qry.Close
151+
return nil, returnAPIError(res.Err), res.Warnings, qry.Close
150152
}
151153

152154
// Optional stats field in response if parameter "stats" is not empty.
@@ -178,26 +180,26 @@ func (api *API) Query(r *http.Request) (data interface{}, warnings []error, erro
178180
}
179181
}
180182
if err != nil {
181-
return nil, res.Warnings, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, qry.Close
183+
return nil, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, res.Warnings, qry.Close
182184
}
183-
return data, res.Warnings, nil, qry.Close
185+
return data, nil, res.Warnings, qry.Close
184186
}
185187

186-
func extractQueryOpts(r *http.Request) (*promql.QueryOpts, error) {
187-
opts := &promql.QueryOpts{
188-
EnablePerStepStats: r.FormValue("stats") == "all",
189-
}
188+
func extractQueryOpts(r *http.Request) (promql.QueryOpts, error) {
189+
var duration time.Duration
190+
190191
if strDuration := r.FormValue("lookback_delta"); strDuration != "" {
191-
duration, err := parseDuration(strDuration)
192+
parsedDuration, err := parseDuration(strDuration)
192193
if err != nil {
193194
return nil, fmt.Errorf("error parsing lookback delta duration: %w", err)
194195
}
195-
opts.LookbackDelta = duration
196+
duration = parsedDuration
196197
}
197-
return opts, nil
198+
199+
return promql.NewPrometheusQueryOpts(r.FormValue("stats") == "all", duration), nil
198200
}
199201

200-
func (api *API) QueryRange(r *http.Request) (data interface{}, warnings []error, error *thanos_api.ApiError, finalizer func()) {
202+
func (api *API) QueryRange(r *http.Request) (data interface{}, error *thanos_api.ApiError, warnings annotations.Annotations, finalizer func()) {
201203
start, err := parseTime(r.FormValue("start"))
202204
if err != nil {
203205
return invalidParamError(err, "start")
@@ -223,7 +225,7 @@ func (api *API) QueryRange(r *http.Request) (data interface{}, warnings []error,
223225
// This is sufficient for 60s resolution for a week or 1h resolution for a year.
224226
if end.Sub(start)/step > 11000 {
225227
err := errors.New("exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)")
226-
return nil, nil, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, nil
228+
return nil, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, nil, nil
227229
}
228230

229231
ctx := r.Context()
@@ -240,7 +242,7 @@ func (api *API) QueryRange(r *http.Request) (data interface{}, warnings []error,
240242

241243
opts, err := extractQueryOpts(r)
242244
if err != nil {
243-
return nil, nil, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, nil
245+
return nil, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, nil, nil
244246
}
245247
qry, err := api.QueryEngine.NewRangeQuery(ctx, api.Queryable, opts, r.FormValue("query"), start, end, step)
246248
if err != nil {
@@ -259,7 +261,7 @@ func (api *API) QueryRange(r *http.Request) (data interface{}, warnings []error,
259261

260262
res := qry.Exec(ctx)
261263
if res.Err != nil {
262-
return nil, res.Warnings, returnAPIError(res.Err), qry.Close
264+
return nil, returnAPIError(res.Err), res.Warnings, qry.Close
263265
}
264266

265267
// Optional stats field in response if parameter "stats" is not empty.
@@ -292,9 +294,9 @@ func (api *API) QueryRange(r *http.Request) (data interface{}, warnings []error,
292294
}
293295

294296
if err != nil {
295-
return nil, res.Warnings, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, qry.Close
297+
return nil, &thanos_api.ApiError{thanos_api.ErrorBadData, err}, res.Warnings, qry.Close
296298
}
297-
return data, res.Warnings, nil, qry.Close
299+
return data, nil, res.Warnings, qry.Close
298300
}
299301

300302
func parseTimeParam(r *http.Request, paramName string, defaultValue time.Time) (time.Time, error) {
@@ -325,9 +327,9 @@ func parseTime(s string) (time.Time, error) {
325327
// Upstream issue: https://github.com/golang/go/issues/20555
326328
switch s {
327329
case minTimeFormatted:
328-
return minTime, nil
330+
return v1.MinTime, nil
329331
case maxTimeFormatted:
330-
return maxTime, nil
332+
return v1.MaxTime, nil
331333
}
332334
return time.Time{}, errors.Errorf("cannot parse %q to a valid timestamp", s)
333335
}
@@ -373,18 +375,13 @@ func returnAPIError(err error) *thanos_api.ApiError {
373375
}
374376

375377
var (
376-
minTime = time.Unix(math.MinInt64/1000+62135596801, 0).UTC()
377-
maxTime = time.Unix(math.MaxInt64/1000-62135596801, 999999999).UTC()
378-
379-
minTimeFormatted = minTime.Format(time.RFC3339Nano)
380-
maxTimeFormatted = maxTime.Format(time.RFC3339Nano)
378+
minTimeFormatted = v1.MinTime.Format(time.RFC3339Nano)
379+
maxTimeFormatted = v1.MaxTime.Format(time.RFC3339Nano)
381380
)
382381

383-
func (api *API) Respond(w http.ResponseWriter, data interface{}, warnings storage.Warnings) {
384-
var warningStrings []string
385-
for _, warning := range warnings {
386-
warningStrings = append(warningStrings, warning.Error())
387-
}
382+
func (api *API) Respond(w http.ResponseWriter, req *http.Request, data interface{}, warnings annotations.Annotations, query string) {
383+
statusMessage := statusSuccess
384+
388385
var b []byte
389386
var err error
390387
switch resp := data.(type) {
@@ -393,26 +390,29 @@ func (api *API) Respond(w http.ResponseWriter, data interface{}, warnings storag
393390
for h, hv := range w.Header() {
394391
resp.Headers = append(resp.Headers, &tripperware.PrometheusResponseHeader{Name: h, Values: hv})
395392
}
393+
resp.Warnings = warnings.AsStrings(query, 10)
396394
b, err = proto.Marshal(resp)
397395
case *instantquery.PrometheusInstantQueryResponse:
398396
w.Header().Set(contentTypeHeader, applicationProtobuf)
399397
for h, hv := range w.Header() {
400398
resp.Headers = append(resp.Headers, &tripperware.PrometheusResponseHeader{Name: h, Values: hv})
401399
}
400+
resp.Warnings = warnings.AsStrings(query, 10)
402401
b, err = proto.Marshal(resp)
403402
case *queryData:
404403
w.Header().Set(contentTypeHeader, applicationJson)
405404
json := jsoniter.ConfigCompatibleWithStandardLibrary
406405
b, err = json.Marshal(&response{
407-
Status: statusSuccess,
406+
Status: statusMessage,
408407
Data: data,
409-
Warnings: warningStrings,
408+
Warnings: warnings.AsStrings(query, 10),
410409
})
411410
default:
412411
level.Error(api.Logger).Log("msg", "error asserting response type")
413412
http.Error(w, "error asserting response type", http.StatusInternalServerError)
414413
return
415414
}
415+
416416
if err != nil {
417417
level.Error(api.Logger).Log("msg", "error marshaling response", "err", err)
418418
http.Error(w, err.Error(), http.StatusInternalServerError)
@@ -558,11 +558,11 @@ func getSampleStreams(data *queryData) *[]tripperware.SampleStream {
558558

559559
for i := 0; i < sampleStreamsLen; i++ {
560560
labelsLen := len(data.Result.(promql.Matrix)[i].Metric)
561-
var labels []github_com_cortexproject_cortex_pkg_cortexpb.LabelAdapter
561+
var labels []cortex_pb.LabelAdapter
562562
if labelsLen > 0 {
563-
labels = make([]github_com_cortexproject_cortex_pkg_cortexpb.LabelAdapter, labelsLen)
563+
labels = make([]cortex_pb.LabelAdapter, labelsLen)
564564
for j := 0; j < labelsLen; j++ {
565-
labels[j] = github_com_cortexproject_cortex_pkg_cortexpb.LabelAdapter{
565+
labels[j] = cortex_pb.LabelAdapter{
566566
Name: data.Result.(promql.Matrix)[i].Metric[j].Name,
567567
Value: data.Result.(promql.Matrix)[i].Metric[j].Value,
568568
}
@@ -591,19 +591,19 @@ func getSamples(data *queryData) *[]*instantquery.Sample {
591591

592592
for i := 0; i < vectorSamplesLen; i++ {
593593
labelsLen := len(data.Result.(promql.Vector)[i].Metric)
594-
var labels []github_com_cortexproject_cortex_pkg_cortexpb.LabelAdapter
594+
var labels []cortex_pb.LabelAdapter
595595
if labelsLen > 0 {
596-
labels = make([]github_com_cortexproject_cortex_pkg_cortexpb.LabelAdapter, labelsLen)
596+
labels = make([]cortex_pb.LabelAdapter, labelsLen)
597597
for j := 0; j < labelsLen; j++ {
598-
labels[j] = github_com_cortexproject_cortex_pkg_cortexpb.LabelAdapter{
598+
labels[j] = cortex_pb.LabelAdapter{
599599
Name: data.Result.(promql.Vector)[i].Metric[j].Name,
600600
Value: data.Result.(promql.Vector)[i].Metric[j].Value,
601601
}
602602
}
603603
}
604604

605605
vectorSamples[i] = &instantquery.Sample{Labels: labels,
606-
Sample: cortexpb.Sample{
606+
Sample: &cortexpb.Sample{
607607
TimestampMs: data.Result.(promql.Vector)[i].T,
608608
Value: data.Result.(promql.Vector)[i].F,
609609
},

0 commit comments

Comments
 (0)