github.com/thanos-io/thanos@v0.32.5/pkg/api/query/v1.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  // Copyright 2016 The Prometheus Authors
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  // This package is a modified copy from
    18  // github.com/prometheus/prometheus/web/api/v1@2121b4628baa7d9d9406aa468712a6a332e77aff.
    19  
    20  package v1
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"math"
    26  	"net/http"
    27  	"sort"
    28  	"strconv"
    29  	"strings"
    30  	"time"
    31  
    32  	"github.com/go-kit/log"
    33  	"github.com/opentracing/opentracing-go"
    34  	"github.com/pkg/errors"
    35  	"github.com/prometheus/client_golang/prometheus"
    36  	"github.com/prometheus/client_golang/prometheus/promauto"
    37  	"github.com/prometheus/common/model"
    38  	"github.com/prometheus/common/route"
    39  	"github.com/prometheus/prometheus/model/labels"
    40  	"github.com/prometheus/prometheus/model/timestamp"
    41  	"github.com/prometheus/prometheus/promql"
    42  	"github.com/prometheus/prometheus/promql/parser"
    43  	"github.com/prometheus/prometheus/storage"
    44  	"github.com/prometheus/prometheus/util/stats"
    45  	v1 "github.com/prometheus/prometheus/web/api/v1"
    46  	promqlapi "github.com/thanos-io/promql-engine/api"
    47  	"github.com/thanos-io/promql-engine/engine"
    48  
    49  	"github.com/thanos-io/thanos/pkg/api"
    50  	"github.com/thanos-io/thanos/pkg/exemplars"
    51  	"github.com/thanos-io/thanos/pkg/exemplars/exemplarspb"
    52  	extpromhttp "github.com/thanos-io/thanos/pkg/extprom/http"
    53  	"github.com/thanos-io/thanos/pkg/gate"
    54  	"github.com/thanos-io/thanos/pkg/logging"
    55  	"github.com/thanos-io/thanos/pkg/metadata"
    56  	"github.com/thanos-io/thanos/pkg/metadata/metadatapb"
    57  	"github.com/thanos-io/thanos/pkg/query"
    58  	"github.com/thanos-io/thanos/pkg/rules"
    59  	"github.com/thanos-io/thanos/pkg/rules/rulespb"
    60  	"github.com/thanos-io/thanos/pkg/runutil"
    61  	"github.com/thanos-io/thanos/pkg/store"
    62  	"github.com/thanos-io/thanos/pkg/store/storepb"
    63  	"github.com/thanos-io/thanos/pkg/targets"
    64  	"github.com/thanos-io/thanos/pkg/targets/targetspb"
    65  	"github.com/thanos-io/thanos/pkg/tenancy"
    66  	"github.com/thanos-io/thanos/pkg/tracing"
    67  )
    68  
    69  const (
    70  	DedupParam               = "dedup"
    71  	PartialResponseParam     = "partial_response"
    72  	MaxSourceResolutionParam = "max_source_resolution"
    73  	ReplicaLabelsParam       = "replicaLabels[]"
    74  	MatcherParam             = "match[]"
    75  	StoreMatcherParam        = "storeMatch[]"
    76  	Step                     = "step"
    77  	Stats                    = "stats"
    78  	ShardInfoParam           = "shard_info"
    79  	LookbackDeltaParam       = "lookback_delta"
    80  	EngineParam              = "engine"
    81  	QueryExplainParam        = "explain"
    82  )
    83  
    84  type PromqlEngineType string
    85  
    86  const (
    87  	PromqlEnginePrometheus PromqlEngineType = "prometheus"
    88  	PromqlEngineThanos     PromqlEngineType = "thanos"
    89  )
    90  
    91  type QueryEngineFactory struct {
    92  	engineOpts            promql.EngineOpts
    93  	remoteEngineEndpoints promqlapi.RemoteEndpoints
    94  
    95  	prometheusEngine v1.QueryEngine
    96  	thanosEngine     v1.QueryEngine
    97  }
    98  
    99  func (f *QueryEngineFactory) GetPrometheusEngine() v1.QueryEngine {
   100  	if f.prometheusEngine != nil {
   101  		return f.prometheusEngine
   102  	}
   103  
   104  	f.prometheusEngine = promql.NewEngine(f.engineOpts)
   105  	return f.prometheusEngine
   106  }
   107  
   108  func (f *QueryEngineFactory) GetThanosEngine() v1.QueryEngine {
   109  	if f.thanosEngine != nil {
   110  		return f.thanosEngine
   111  	}
   112  
   113  	if f.remoteEngineEndpoints == nil {
   114  		f.thanosEngine = engine.New(engine.Opts{EngineOpts: f.engineOpts, Engine: f.GetPrometheusEngine()})
   115  	} else {
   116  		f.thanosEngine = engine.NewDistributedEngine(engine.Opts{EngineOpts: f.engineOpts, Engine: f.GetPrometheusEngine()}, f.remoteEngineEndpoints)
   117  	}
   118  
   119  	return f.thanosEngine
   120  }
   121  
   122  func NewQueryEngineFactory(
   123  	engineOpts promql.EngineOpts,
   124  	remoteEngineEndpoints promqlapi.RemoteEndpoints,
   125  ) *QueryEngineFactory {
   126  	return &QueryEngineFactory{
   127  		engineOpts:            engineOpts,
   128  		remoteEngineEndpoints: remoteEngineEndpoints,
   129  	}
   130  }
   131  
   132  // QueryAPI is an API used by Thanos Querier.
   133  type QueryAPI struct {
   134  	baseAPI         *api.BaseAPI
   135  	logger          log.Logger
   136  	gate            gate.Gate
   137  	queryableCreate query.QueryableCreator
   138  	// queryEngine returns appropriate promql.Engine for a query with a given step.
   139  	engineFactory       *QueryEngineFactory
   140  	defaultEngine       PromqlEngineType
   141  	lookbackDeltaCreate func(int64) time.Duration
   142  	ruleGroups          rules.UnaryClient
   143  	targets             targets.UnaryClient
   144  	metadatas           metadata.UnaryClient
   145  	exemplars           exemplars.UnaryClient
   146  
   147  	enableAutodownsampling              bool
   148  	enableQueryPartialResponse          bool
   149  	enableRulePartialResponse           bool
   150  	enableTargetPartialResponse         bool
   151  	enableMetricMetadataPartialResponse bool
   152  	enableExemplarPartialResponse       bool
   153  	enableQueryPushdown                 bool
   154  	disableCORS                         bool
   155  
   156  	replicaLabels  []string
   157  	endpointStatus func() []query.EndpointStatus
   158  
   159  	defaultRangeQueryStep                  time.Duration
   160  	defaultInstantQueryMaxSourceResolution time.Duration
   161  	defaultMetadataTimeRange               time.Duration
   162  
   163  	queryRangeHist prometheus.Histogram
   164  
   165  	seriesStatsAggregatorFactory store.SeriesQueryPerformanceMetricsAggregatorFactory
   166  
   167  	tenantHeader    string
   168  	defaultTenant   string
   169  	tenantCertField string
   170  }
   171  
   172  // NewQueryAPI returns an initialized QueryAPI type.
   173  func NewQueryAPI(
   174  	logger log.Logger,
   175  	endpointStatus func() []query.EndpointStatus,
   176  	engineFactory *QueryEngineFactory,
   177  	defaultEngine PromqlEngineType,
   178  	lookbackDeltaCreate func(int64) time.Duration,
   179  	c query.QueryableCreator,
   180  	ruleGroups rules.UnaryClient,
   181  	targets targets.UnaryClient,
   182  	metadatas metadata.UnaryClient,
   183  	exemplars exemplars.UnaryClient,
   184  	enableAutodownsampling bool,
   185  	enableQueryPartialResponse bool,
   186  	enableRulePartialResponse bool,
   187  	enableTargetPartialResponse bool,
   188  	enableMetricMetadataPartialResponse bool,
   189  	enableExemplarPartialResponse bool,
   190  	enableQueryPushdown bool,
   191  	replicaLabels []string,
   192  	flagsMap map[string]string,
   193  	defaultRangeQueryStep time.Duration,
   194  	defaultInstantQueryMaxSourceResolution time.Duration,
   195  	defaultMetadataTimeRange time.Duration,
   196  	disableCORS bool,
   197  	gate gate.Gate,
   198  	statsAggregatorFactory store.SeriesQueryPerformanceMetricsAggregatorFactory,
   199  	reg *prometheus.Registry,
   200  	tenantHeader string,
   201  	defaultTenant string,
   202  	tenantCertField string,
   203  ) *QueryAPI {
   204  	if statsAggregatorFactory == nil {
   205  		statsAggregatorFactory = &store.NoopSeriesStatsAggregatorFactory{}
   206  	}
   207  	return &QueryAPI{
   208  		baseAPI:                                api.NewBaseAPI(logger, disableCORS, flagsMap),
   209  		logger:                                 logger,
   210  		engineFactory:                          engineFactory,
   211  		defaultEngine:                          defaultEngine,
   212  		lookbackDeltaCreate:                    lookbackDeltaCreate,
   213  		queryableCreate:                        c,
   214  		gate:                                   gate,
   215  		ruleGroups:                             ruleGroups,
   216  		targets:                                targets,
   217  		metadatas:                              metadatas,
   218  		exemplars:                              exemplars,
   219  		enableAutodownsampling:                 enableAutodownsampling,
   220  		enableQueryPartialResponse:             enableQueryPartialResponse,
   221  		enableRulePartialResponse:              enableRulePartialResponse,
   222  		enableTargetPartialResponse:            enableTargetPartialResponse,
   223  		enableMetricMetadataPartialResponse:    enableMetricMetadataPartialResponse,
   224  		enableExemplarPartialResponse:          enableExemplarPartialResponse,
   225  		enableQueryPushdown:                    enableQueryPushdown,
   226  		replicaLabels:                          replicaLabels,
   227  		endpointStatus:                         endpointStatus,
   228  		defaultRangeQueryStep:                  defaultRangeQueryStep,
   229  		defaultInstantQueryMaxSourceResolution: defaultInstantQueryMaxSourceResolution,
   230  		defaultMetadataTimeRange:               defaultMetadataTimeRange,
   231  		disableCORS:                            disableCORS,
   232  		seriesStatsAggregatorFactory:           statsAggregatorFactory,
   233  		tenantHeader:                           tenantHeader,
   234  		defaultTenant:                          defaultTenant,
   235  		tenantCertField:                        tenantCertField,
   236  
   237  		queryRangeHist: promauto.With(reg).NewHistogram(prometheus.HistogramOpts{
   238  			Name:    "thanos_query_range_requested_timespan_duration_seconds",
   239  			Help:    "A histogram of the query range window in seconds",
   240  			Buckets: prometheus.ExponentialBuckets(15*60, 2, 12),
   241  		}),
   242  	}
   243  }
   244  
   245  // Register the API's endpoints in the given router.
   246  func (qapi *QueryAPI) Register(r *route.Router, tracer opentracing.Tracer, logger log.Logger, ins extpromhttp.InstrumentationMiddleware, logMiddleware *logging.HTTPServerMiddleware) {
   247  	qapi.baseAPI.Register(r, tracer, logger, ins, logMiddleware)
   248  
   249  	instr := api.GetInstr(tracer, logger, ins, logMiddleware, qapi.disableCORS)
   250  
   251  	r.Get("/query", instr("query", qapi.query))
   252  	r.Post("/query", instr("query", qapi.query))
   253  
   254  	r.Get("/query_range", instr("query_range", qapi.queryRange))
   255  	r.Post("/query_range", instr("query_range", qapi.queryRange))
   256  
   257  	r.Get("/label/:name/values", instr("label_values", qapi.labelValues))
   258  
   259  	r.Get("/series", instr("series", qapi.series))
   260  	r.Post("/series", instr("series", qapi.series))
   261  
   262  	r.Get("/labels", instr("label_names", qapi.labelNames))
   263  	r.Post("/labels", instr("label_names", qapi.labelNames))
   264  
   265  	r.Get("/stores", instr("stores", qapi.stores))
   266  
   267  	r.Get("/alerts", instr("alerts", NewAlertsHandler(qapi.ruleGroups, qapi.enableRulePartialResponse)))
   268  	r.Get("/rules", instr("rules", NewRulesHandler(qapi.ruleGroups, qapi.enableRulePartialResponse)))
   269  
   270  	r.Get("/targets", instr("targets", NewTargetsHandler(qapi.targets, qapi.enableTargetPartialResponse)))
   271  
   272  	r.Get("/metadata", instr("metadata", NewMetricMetadataHandler(qapi.metadatas, qapi.enableMetricMetadataPartialResponse)))
   273  
   274  	r.Get("/query_exemplars", instr("exemplars", NewExemplarsHandler(qapi.exemplars, qapi.enableExemplarPartialResponse)))
   275  	r.Post("/query_exemplars", instr("exemplars", NewExemplarsHandler(qapi.exemplars, qapi.enableExemplarPartialResponse)))
   276  }
   277  
   278  type queryData struct {
   279  	ResultType parser.ValueType `json:"resultType"`
   280  	Result     parser.Value     `json:"result"`
   281  	Stats      stats.QueryStats `json:"stats,omitempty"`
   282  	// Additional Thanos Response field.
   283  	QueryExplanation *engine.ExplainOutputNode `json:"explanation,omitempty"`
   284  	Warnings         []error                   `json:"warnings,omitempty"`
   285  }
   286  
   287  func (qapi *QueryAPI) parseEnableDedupParam(r *http.Request) (enableDeduplication bool, _ *api.ApiError) {
   288  	enableDeduplication = true
   289  
   290  	if val := r.FormValue(DedupParam); val != "" {
   291  		var err error
   292  		enableDeduplication, err = strconv.ParseBool(val)
   293  		if err != nil {
   294  			return false, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", DedupParam)}
   295  		}
   296  	}
   297  	return enableDeduplication, nil
   298  }
   299  
   300  func (qapi *QueryAPI) parseEngineParam(r *http.Request) (queryEngine v1.QueryEngine, _ *api.ApiError) {
   301  	var engine v1.QueryEngine
   302  
   303  	param := PromqlEngineType(r.FormValue("engine"))
   304  	if param == "" {
   305  		param = qapi.defaultEngine
   306  	}
   307  
   308  	switch param {
   309  	case PromqlEnginePrometheus:
   310  		engine = qapi.engineFactory.GetPrometheusEngine()
   311  	case PromqlEngineThanos:
   312  		engine = qapi.engineFactory.GetThanosEngine()
   313  	default:
   314  		return nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("'%s' bad engine", param)}
   315  	}
   316  
   317  	return engine, nil
   318  }
   319  
   320  func (qapi *QueryAPI) parseReplicaLabelsParam(r *http.Request) (replicaLabels []string, _ *api.ApiError) {
   321  	if err := r.ParseForm(); err != nil {
   322  		return nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form")}
   323  	}
   324  
   325  	replicaLabels = qapi.replicaLabels
   326  	// Overwrite the cli flag when provided as a query parameter.
   327  	if len(r.Form[ReplicaLabelsParam]) > 0 {
   328  		replicaLabels = r.Form[ReplicaLabelsParam]
   329  	}
   330  
   331  	return replicaLabels, nil
   332  }
   333  
   334  func (qapi *QueryAPI) parseStoreDebugMatchersParam(r *http.Request) (storeMatchers [][]*labels.Matcher, _ *api.ApiError) {
   335  	if err := r.ParseForm(); err != nil {
   336  		return nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form")}
   337  	}
   338  
   339  	for _, s := range r.Form[StoreMatcherParam] {
   340  		matchers, err := parser.ParseMetricSelector(s)
   341  		if err != nil {
   342  			return nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}
   343  		}
   344  		storeMatchers = append(storeMatchers, matchers)
   345  	}
   346  
   347  	return storeMatchers, nil
   348  }
   349  
   350  func (qapi *QueryAPI) parseLookbackDeltaParam(r *http.Request) (time.Duration, *api.ApiError) {
   351  	// Overwrite the cli flag when provided as a query parameter.
   352  	if val := r.FormValue(LookbackDeltaParam); val != "" {
   353  		var err error
   354  		lookbackDelta, err := parseDuration(val)
   355  		if err != nil {
   356  			return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", LookbackDeltaParam)}
   357  		}
   358  		return lookbackDelta, nil
   359  	}
   360  	// If duration 0 is returned, lookback delta is taken from engine config.
   361  	return time.Duration(0), nil
   362  }
   363  
   364  func (qapi *QueryAPI) parseDownsamplingParamMillis(r *http.Request, defaultVal time.Duration) (maxResolutionMillis int64, _ *api.ApiError) {
   365  	maxSourceResolution := 0 * time.Second
   366  
   367  	val := r.FormValue(MaxSourceResolutionParam)
   368  	if qapi.enableAutodownsampling || (val == "auto") {
   369  		maxSourceResolution = defaultVal
   370  	}
   371  	if val != "" && val != "auto" {
   372  		var err error
   373  		maxSourceResolution, err = parseDuration(val)
   374  		if err != nil {
   375  			return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", MaxSourceResolutionParam)}
   376  		}
   377  	}
   378  
   379  	if maxSourceResolution < 0 {
   380  		return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("negative '%s' is not accepted. Try a positive integer", MaxSourceResolutionParam)}
   381  	}
   382  
   383  	return int64(maxSourceResolution / time.Millisecond), nil
   384  }
   385  
   386  func (qapi *QueryAPI) parsePartialResponseParam(r *http.Request, defaultEnablePartialResponse bool) (enablePartialResponse bool, _ *api.ApiError) {
   387  	// Overwrite the cli flag when provided as a query parameter.
   388  	if val := r.FormValue(PartialResponseParam); val != "" {
   389  		var err error
   390  		defaultEnablePartialResponse, err = strconv.ParseBool(val)
   391  		if err != nil {
   392  			return false, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", PartialResponseParam)}
   393  		}
   394  	}
   395  	return defaultEnablePartialResponse, nil
   396  }
   397  
   398  func (qapi *QueryAPI) parseStep(r *http.Request, defaultRangeQueryStep time.Duration, rangeSeconds int64) (time.Duration, *api.ApiError) {
   399  	// Overwrite the cli flag when provided as a query parameter.
   400  	if val := r.FormValue(Step); val != "" {
   401  		var err error
   402  		defaultRangeQueryStep, err = parseDuration(val)
   403  		if err != nil {
   404  			return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", Step)}
   405  		}
   406  		return defaultRangeQueryStep, nil
   407  	}
   408  	// Default step is used this way to make it consistent with UI.
   409  	d := time.Duration(math.Max(float64(rangeSeconds/250), float64(defaultRangeQueryStep/time.Second))) * time.Second
   410  	return d, nil
   411  }
   412  
   413  func (qapi *QueryAPI) parseShardInfo(r *http.Request) (*storepb.ShardInfo, *api.ApiError) {
   414  	data := r.FormValue(ShardInfoParam)
   415  	if data == "" {
   416  		return nil, nil
   417  	}
   418  
   419  	if len(data) == 0 {
   420  		return nil, nil
   421  	}
   422  
   423  	var info storepb.ShardInfo
   424  	if err := json.Unmarshal([]byte(data), &info); err != nil {
   425  		return nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "could not unmarshal parameter %s", ShardInfoParam)}
   426  	}
   427  
   428  	return &info, nil
   429  }
   430  
   431  func (qapi *QueryAPI) parseQueryExplainParam(r *http.Request, query promql.Query) (*engine.ExplainOutputNode, *api.ApiError) {
   432  	var explanation *engine.ExplainOutputNode
   433  
   434  	if val := r.FormValue(QueryExplainParam); val != "" {
   435  		var err error
   436  		enableExplanation, err := strconv.ParseBool(val)
   437  		if err != nil {
   438  			return explanation, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter", QueryExplainParam)}
   439  		}
   440  		if enableExplanation {
   441  			if eq, ok := query.(engine.ExplainableQuery); ok {
   442  				explanation = eq.Explain()
   443  			} else {
   444  				return explanation, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("Query not explainable")}
   445  			}
   446  		}
   447  	}
   448  
   449  	return explanation, nil
   450  }
   451  
   452  func (qapi *QueryAPI) query(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
   453  	ts, err := parseTimeParam(r, "time", qapi.baseAPI.Now())
   454  	if err != nil {
   455  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   456  	}
   457  
   458  	ctx := r.Context()
   459  	if to := r.FormValue("timeout"); to != "" {
   460  		var cancel context.CancelFunc
   461  		timeout, err := parseDuration(to)
   462  		if err != nil {
   463  			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   464  		}
   465  
   466  		ctx, cancel = context.WithTimeout(ctx, timeout)
   467  		defer cancel()
   468  	}
   469  
   470  	enableDedup, apiErr := qapi.parseEnableDedupParam(r)
   471  	if apiErr != nil {
   472  		return nil, nil, apiErr, func() {}
   473  	}
   474  
   475  	replicaLabels, apiErr := qapi.parseReplicaLabelsParam(r)
   476  	if apiErr != nil {
   477  		return nil, nil, apiErr, func() {}
   478  	}
   479  
   480  	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
   481  	if apiErr != nil {
   482  		return nil, nil, apiErr, func() {}
   483  	}
   484  
   485  	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
   486  	if apiErr != nil {
   487  		return nil, nil, apiErr, func() {}
   488  	}
   489  
   490  	maxSourceResolution, apiErr := qapi.parseDownsamplingParamMillis(r, qapi.defaultInstantQueryMaxSourceResolution)
   491  	if apiErr != nil {
   492  		return nil, nil, apiErr, func() {}
   493  	}
   494  
   495  	shardInfo, apiErr := qapi.parseShardInfo(r)
   496  	if apiErr != nil {
   497  		return nil, nil, apiErr, func() {}
   498  	}
   499  
   500  	engine, apiErr := qapi.parseEngineParam(r)
   501  	if apiErr != nil {
   502  		return nil, nil, apiErr, func() {}
   503  	}
   504  
   505  	lookbackDelta := qapi.lookbackDeltaCreate(maxSourceResolution)
   506  	// Get custom lookback delta from request.
   507  	lookbackDeltaFromReq, apiErr := qapi.parseLookbackDeltaParam(r)
   508  	if apiErr != nil {
   509  		return nil, nil, apiErr, func() {}
   510  	}
   511  	if lookbackDeltaFromReq > 0 {
   512  		lookbackDelta = lookbackDeltaFromReq
   513  	}
   514  
   515  	tenant, err := tenancy.GetTenantFromHTTP(r, qapi.tenantHeader, qapi.defaultTenant, qapi.tenantCertField)
   516  	if err != nil {
   517  		apiErr = &api.ApiError{Typ: api.ErrorBadData, Err: err}
   518  		return nil, nil, apiErr, func() {}
   519  	}
   520  	ctx = context.WithValue(ctx, tenancy.TenantKey, tenant)
   521  
   522  	// We are starting promQL tracing span here, because we have no control over promQL code.
   523  	span, ctx := tracing.StartSpan(ctx, "promql_instant_query")
   524  	defer span.Finish()
   525  
   526  	var seriesStats []storepb.SeriesStatsCounter
   527  	qry, err := engine.NewInstantQuery(
   528  		ctx,
   529  		qapi.queryableCreate(
   530  			enableDedup,
   531  			replicaLabels,
   532  			storeDebugMatchers,
   533  			maxSourceResolution,
   534  			enablePartialResponse,
   535  			qapi.enableQueryPushdown,
   536  			false,
   537  			shardInfo,
   538  			query.NewAggregateStatsReporter(&seriesStats),
   539  		),
   540  		promql.NewPrometheusQueryOpts(false, lookbackDelta),
   541  		r.FormValue("query"),
   542  		ts,
   543  	)
   544  
   545  	if err != nil {
   546  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   547  	}
   548  
   549  	explanation, apiErr := qapi.parseQueryExplainParam(r, qry)
   550  	if apiErr != nil {
   551  		return nil, nil, apiErr, func() {}
   552  	}
   553  
   554  	tracing.DoInSpan(ctx, "query_gate_ismyturn", func(ctx context.Context) {
   555  		err = qapi.gate.Start(ctx)
   556  	})
   557  	if err != nil {
   558  		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}, qry.Close
   559  	}
   560  	defer qapi.gate.Done()
   561  
   562  	beforeRange := time.Now()
   563  	res := qry.Exec(ctx)
   564  	if res.Err != nil {
   565  		switch res.Err.(type) {
   566  		case promql.ErrQueryCanceled:
   567  			return nil, nil, &api.ApiError{Typ: api.ErrorCanceled, Err: res.Err}, qry.Close
   568  		case promql.ErrQueryTimeout:
   569  			return nil, nil, &api.ApiError{Typ: api.ErrorTimeout, Err: res.Err}, qry.Close
   570  		case promql.ErrStorage:
   571  			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: res.Err}, qry.Close
   572  		}
   573  		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: res.Err}, qry.Close
   574  	}
   575  
   576  	aggregator := qapi.seriesStatsAggregatorFactory.NewAggregator()
   577  	for i := range seriesStats {
   578  		aggregator.Aggregate(seriesStats[i])
   579  	}
   580  	aggregator.Observe(time.Since(beforeRange).Seconds())
   581  
   582  	// Optional stats field in response if parameter "stats" is not empty.
   583  	var qs stats.QueryStats
   584  	if r.FormValue(Stats) != "" {
   585  		qs = stats.NewQueryStats(qry.Stats())
   586  	}
   587  	return &queryData{
   588  		ResultType:       res.Value.Type(),
   589  		Result:           res.Value,
   590  		Stats:            qs,
   591  		QueryExplanation: explanation,
   592  	}, res.Warnings, nil, qry.Close
   593  }
   594  
   595  func (qapi *QueryAPI) queryRange(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
   596  	start, err := parseTime(r.FormValue("start"))
   597  	if err != nil {
   598  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   599  	}
   600  	end, err := parseTime(r.FormValue("end"))
   601  	if err != nil {
   602  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   603  	}
   604  	if end.Before(start) {
   605  		err := errors.New("end timestamp must not be before start time")
   606  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   607  	}
   608  
   609  	step, apiErr := qapi.parseStep(r, qapi.defaultRangeQueryStep, int64(end.Sub(start)/time.Second))
   610  	if apiErr != nil {
   611  		return nil, nil, apiErr, func() {}
   612  	}
   613  
   614  	if step <= 0 {
   615  		err := errors.New("zero or negative query resolution step widths are not accepted. Try a positive integer")
   616  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   617  	}
   618  
   619  	// For safety, limit the number of returned points per timeseries.
   620  	// This is sufficient for 60s resolution for a week or 1h resolution for a year.
   621  	if end.Sub(start)/step > 11000 {
   622  		err := errors.New("exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)")
   623  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   624  	}
   625  
   626  	ctx := r.Context()
   627  	if to := r.FormValue("timeout"); to != "" {
   628  		var cancel context.CancelFunc
   629  		timeout, err := parseDuration(to)
   630  		if err != nil {
   631  			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   632  		}
   633  
   634  		ctx, cancel = context.WithTimeout(ctx, timeout)
   635  		defer cancel()
   636  	}
   637  
   638  	enableDedup, apiErr := qapi.parseEnableDedupParam(r)
   639  	if apiErr != nil {
   640  		return nil, nil, apiErr, func() {}
   641  	}
   642  
   643  	replicaLabels, apiErr := qapi.parseReplicaLabelsParam(r)
   644  	if apiErr != nil {
   645  		return nil, nil, apiErr, func() {}
   646  	}
   647  
   648  	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
   649  	if apiErr != nil {
   650  		return nil, nil, apiErr, func() {}
   651  	}
   652  
   653  	// If no max_source_resolution is specified fit at least 5 samples between steps.
   654  	maxSourceResolution, apiErr := qapi.parseDownsamplingParamMillis(r, step/5)
   655  	if apiErr != nil {
   656  		return nil, nil, apiErr, func() {}
   657  	}
   658  
   659  	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
   660  	if apiErr != nil {
   661  		return nil, nil, apiErr, func() {}
   662  	}
   663  
   664  	shardInfo, apiErr := qapi.parseShardInfo(r)
   665  	if apiErr != nil {
   666  		return nil, nil, apiErr, func() {}
   667  	}
   668  
   669  	engine, apiErr := qapi.parseEngineParam(r)
   670  	if apiErr != nil {
   671  		return nil, nil, apiErr, func() {}
   672  	}
   673  
   674  	lookbackDelta := qapi.lookbackDeltaCreate(maxSourceResolution)
   675  	// Get custom lookback delta from request.
   676  	lookbackDeltaFromReq, apiErr := qapi.parseLookbackDeltaParam(r)
   677  	if apiErr != nil {
   678  		return nil, nil, apiErr, func() {}
   679  	}
   680  	if lookbackDeltaFromReq > 0 {
   681  		lookbackDelta = lookbackDeltaFromReq
   682  	}
   683  
   684  	tenant, err := tenancy.GetTenantFromHTTP(r, qapi.tenantHeader, qapi.defaultTenant, qapi.tenantCertField)
   685  	if err != nil {
   686  		apiErr = &api.ApiError{Typ: api.ErrorBadData, Err: err}
   687  		return nil, nil, apiErr, func() {}
   688  	}
   689  	ctx = context.WithValue(ctx, tenancy.TenantKey, tenant)
   690  
   691  	// Record the query range requested.
   692  	qapi.queryRangeHist.Observe(end.Sub(start).Seconds())
   693  
   694  	// We are starting promQL tracing span here, because we have no control over promQL code.
   695  	span, ctx := tracing.StartSpan(ctx, "promql_range_query")
   696  	defer span.Finish()
   697  
   698  	var seriesStats []storepb.SeriesStatsCounter
   699  	qry, err := engine.NewRangeQuery(
   700  		ctx,
   701  		qapi.queryableCreate(
   702  			enableDedup,
   703  			replicaLabels,
   704  			storeDebugMatchers,
   705  			maxSourceResolution,
   706  			enablePartialResponse,
   707  			qapi.enableQueryPushdown,
   708  			false,
   709  			shardInfo,
   710  			query.NewAggregateStatsReporter(&seriesStats),
   711  		),
   712  		promql.NewPrometheusQueryOpts(false, lookbackDelta),
   713  		r.FormValue("query"),
   714  		start,
   715  		end,
   716  		step,
   717  	)
   718  	if err != nil {
   719  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   720  	}
   721  
   722  	explanation, apiErr := qapi.parseQueryExplainParam(r, qry)
   723  	if apiErr != nil {
   724  		return nil, nil, apiErr, func() {}
   725  	}
   726  
   727  	tracing.DoInSpan(ctx, "query_gate_ismyturn", func(ctx context.Context) {
   728  		err = qapi.gate.Start(ctx)
   729  	})
   730  	if err != nil {
   731  		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}, qry.Close
   732  	}
   733  	defer qapi.gate.Done()
   734  
   735  	beforeRange := time.Now()
   736  	res := qry.Exec(ctx)
   737  	if res.Err != nil {
   738  		switch res.Err.(type) {
   739  		case promql.ErrQueryCanceled:
   740  			return nil, nil, &api.ApiError{Typ: api.ErrorCanceled, Err: res.Err}, qry.Close
   741  		case promql.ErrQueryTimeout:
   742  			return nil, nil, &api.ApiError{Typ: api.ErrorTimeout, Err: res.Err}, qry.Close
   743  		}
   744  		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: res.Err}, qry.Close
   745  	}
   746  	aggregator := qapi.seriesStatsAggregatorFactory.NewAggregator()
   747  	for i := range seriesStats {
   748  		aggregator.Aggregate(seriesStats[i])
   749  	}
   750  	aggregator.Observe(time.Since(beforeRange).Seconds())
   751  
   752  	// Optional stats field in response if parameter "stats" is not empty.
   753  	var qs stats.QueryStats
   754  	if r.FormValue(Stats) != "" {
   755  		qs = stats.NewQueryStats(qry.Stats())
   756  	}
   757  	return &queryData{
   758  		ResultType:       res.Value.Type(),
   759  		Result:           res.Value,
   760  		Stats:            qs,
   761  		QueryExplanation: explanation,
   762  	}, res.Warnings, nil, qry.Close
   763  }
   764  
   765  func (qapi *QueryAPI) labelValues(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
   766  	ctx := r.Context()
   767  	name := route.Param(ctx, "name")
   768  
   769  	if !model.LabelNameRE.MatchString(name) {
   770  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("invalid label name: %q", name)}, func() {}
   771  	}
   772  
   773  	start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange)
   774  	if err != nil {
   775  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   776  	}
   777  
   778  	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
   779  	if apiErr != nil {
   780  		return nil, nil, apiErr, func() {}
   781  	}
   782  
   783  	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
   784  	if apiErr != nil {
   785  		return nil, nil, apiErr, func() {}
   786  	}
   787  
   788  	var matcherSets [][]*labels.Matcher
   789  	for _, s := range r.Form[MatcherParam] {
   790  		matchers, err := parser.ParseMetricSelector(s)
   791  		if err != nil {
   792  			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   793  		}
   794  		matcherSets = append(matcherSets, matchers)
   795  	}
   796  
   797  	tenant, err := tenancy.GetTenantFromHTTP(r, qapi.tenantHeader, qapi.defaultTenant, qapi.tenantCertField)
   798  	if err != nil {
   799  		apiErr = &api.ApiError{Typ: api.ErrorBadData, Err: err}
   800  		return nil, nil, apiErr, func() {}
   801  	}
   802  	ctx = context.WithValue(ctx, tenancy.TenantKey, tenant)
   803  
   804  	q, err := qapi.queryableCreate(
   805  		true,
   806  		nil,
   807  		storeDebugMatchers,
   808  		0,
   809  		enablePartialResponse,
   810  		qapi.enableQueryPushdown,
   811  		true,
   812  		nil,
   813  		query.NoopSeriesStatsReporter,
   814  	).Querier(ctx, timestamp.FromTime(start), timestamp.FromTime(end))
   815  	if err != nil {
   816  		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}, func() {}
   817  	}
   818  	defer runutil.CloseWithLogOnErr(qapi.logger, q, "queryable labelValues")
   819  
   820  	var (
   821  		vals     []string
   822  		warnings storage.Warnings
   823  	)
   824  	if len(matcherSets) > 0 {
   825  		var callWarnings storage.Warnings
   826  		labelValuesSet := make(map[string]struct{})
   827  		for _, matchers := range matcherSets {
   828  			vals, callWarnings, err = q.LabelValues(name, matchers...)
   829  			if err != nil {
   830  				return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}, func() {}
   831  			}
   832  			warnings = append(warnings, callWarnings...)
   833  			for _, val := range vals {
   834  				labelValuesSet[val] = struct{}{}
   835  			}
   836  		}
   837  
   838  		vals = make([]string, 0, len(labelValuesSet))
   839  		for val := range labelValuesSet {
   840  			vals = append(vals, val)
   841  		}
   842  		sort.Strings(vals)
   843  	} else {
   844  		vals, warnings, err = q.LabelValues(name)
   845  		if err != nil {
   846  			return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}, func() {}
   847  		}
   848  	}
   849  
   850  	if vals == nil {
   851  		vals = make([]string, 0)
   852  	}
   853  
   854  	return vals, warnings, nil, func() {}
   855  }
   856  
   857  func (qapi *QueryAPI) series(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
   858  	if err := r.ParseForm(); err != nil {
   859  		return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form")}, func() {}
   860  	}
   861  
   862  	if len(r.Form[MatcherParam]) == 0 {
   863  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("no match[] parameter provided")}, func() {}
   864  	}
   865  
   866  	start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange)
   867  	if err != nil {
   868  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   869  	}
   870  
   871  	var matcherSets [][]*labels.Matcher
   872  	for _, s := range r.Form[MatcherParam] {
   873  		matchers, err := parser.ParseMetricSelector(s)
   874  		if err != nil {
   875  			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   876  		}
   877  		matcherSets = append(matcherSets, matchers)
   878  	}
   879  
   880  	enableDedup, apiErr := qapi.parseEnableDedupParam(r)
   881  	if apiErr != nil {
   882  		return nil, nil, apiErr, func() {}
   883  	}
   884  
   885  	replicaLabels, apiErr := qapi.parseReplicaLabelsParam(r)
   886  	if apiErr != nil {
   887  		return nil, nil, apiErr, func() {}
   888  	}
   889  
   890  	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
   891  	if apiErr != nil {
   892  		return nil, nil, apiErr, func() {}
   893  	}
   894  
   895  	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
   896  	if apiErr != nil {
   897  		return nil, nil, apiErr, func() {}
   898  	}
   899  
   900  	tenant, err := tenancy.GetTenantFromHTTP(r, qapi.tenantHeader, qapi.defaultTenant, "")
   901  	if err != nil {
   902  		apiErr = &api.ApiError{Typ: api.ErrorBadData, Err: err}
   903  		return nil, nil, apiErr, func() {}
   904  	}
   905  	ctx := context.WithValue(r.Context(), tenancy.TenantKey, tenant)
   906  
   907  	q, err := qapi.queryableCreate(
   908  		enableDedup,
   909  		replicaLabels,
   910  		storeDebugMatchers,
   911  		math.MaxInt64,
   912  		enablePartialResponse,
   913  		qapi.enableQueryPushdown,
   914  		true,
   915  		nil,
   916  		query.NoopSeriesStatsReporter,
   917  	).Querier(ctx, timestamp.FromTime(start), timestamp.FromTime(end))
   918  
   919  	if err != nil {
   920  		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}, func() {}
   921  	}
   922  	defer runutil.CloseWithLogOnErr(qapi.logger, q, "queryable series")
   923  
   924  	var (
   925  		metrics = []labels.Labels{}
   926  		sets    []storage.SeriesSet
   927  	)
   928  	for _, mset := range matcherSets {
   929  		sets = append(sets, q.Select(false, nil, mset...))
   930  	}
   931  
   932  	set := storage.NewMergeSeriesSet(sets, storage.ChainedSeriesMerge)
   933  	for set.Next() {
   934  		metrics = append(metrics, set.At().Labels())
   935  	}
   936  	if set.Err() != nil {
   937  		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: set.Err()}, func() {}
   938  	}
   939  	return metrics, set.Warnings(), nil, func() {}
   940  }
   941  
   942  func (qapi *QueryAPI) labelNames(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
   943  	start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange)
   944  	if err != nil {
   945  		return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   946  	}
   947  
   948  	enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse)
   949  	if apiErr != nil {
   950  		return nil, nil, apiErr, func() {}
   951  	}
   952  
   953  	storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r)
   954  	if apiErr != nil {
   955  		return nil, nil, apiErr, func() {}
   956  	}
   957  
   958  	var matcherSets [][]*labels.Matcher
   959  	for _, s := range r.Form[MatcherParam] {
   960  		matchers, err := parser.ParseMetricSelector(s)
   961  		if err != nil {
   962  			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
   963  		}
   964  		matcherSets = append(matcherSets, matchers)
   965  	}
   966  
   967  	tenant, err := tenancy.GetTenantFromHTTP(r, qapi.tenantHeader, qapi.defaultTenant, "")
   968  	if err != nil {
   969  		apiErr = &api.ApiError{Typ: api.ErrorBadData, Err: err}
   970  		return nil, nil, apiErr, func() {}
   971  	}
   972  	ctx := context.WithValue(r.Context(), tenancy.TenantKey, tenant)
   973  
   974  	q, err := qapi.queryableCreate(
   975  		true,
   976  		nil,
   977  		storeDebugMatchers,
   978  		0,
   979  		enablePartialResponse,
   980  		qapi.enableQueryPushdown,
   981  		true,
   982  		nil,
   983  		query.NoopSeriesStatsReporter,
   984  	).Querier(ctx, timestamp.FromTime(start), timestamp.FromTime(end))
   985  	if err != nil {
   986  		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}, func() {}
   987  	}
   988  	defer runutil.CloseWithLogOnErr(qapi.logger, q, "queryable labelNames")
   989  
   990  	var (
   991  		names    []string
   992  		warnings storage.Warnings
   993  	)
   994  
   995  	if len(matcherSets) > 0 {
   996  		var callWarnings storage.Warnings
   997  		labelNamesSet := make(map[string]struct{})
   998  		for _, matchers := range matcherSets {
   999  			names, callWarnings, err = q.LabelNames(matchers...)
  1000  			if err != nil {
  1001  				return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}, func() {}
  1002  			}
  1003  			warnings = append(warnings, callWarnings...)
  1004  			for _, val := range names {
  1005  				labelNamesSet[val] = struct{}{}
  1006  			}
  1007  		}
  1008  
  1009  		names = make([]string, 0, len(labelNamesSet))
  1010  		for name := range labelNamesSet {
  1011  			names = append(names, name)
  1012  		}
  1013  		sort.Strings(names)
  1014  	} else {
  1015  		names, warnings, err = q.LabelNames()
  1016  	}
  1017  
  1018  	if err != nil {
  1019  		return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err}, func() {}
  1020  	}
  1021  	if names == nil {
  1022  		names = make([]string, 0)
  1023  	}
  1024  
  1025  	return names, warnings, nil, func() {}
  1026  }
  1027  
  1028  func (qapi *QueryAPI) stores(_ *http.Request) (interface{}, []error, *api.ApiError, func()) {
  1029  	statuses := make(map[string][]query.EndpointStatus)
  1030  	for _, status := range qapi.endpointStatus() {
  1031  		// Don't consider an endpoint if we cannot retrieve component type.
  1032  		if status.ComponentType == nil {
  1033  			continue
  1034  		}
  1035  		statuses[status.ComponentType.String()] = append(statuses[status.ComponentType.String()], status)
  1036  	}
  1037  	return statuses, nil, nil, func() {}
  1038  }
  1039  
  1040  // NewTargetsHandler created handler compatible with HTTP /api/v1/targets https://prometheus.io/docs/prometheus/latest/querying/api/#targets
  1041  // which uses gRPC Unary Targets API.
  1042  func NewTargetsHandler(client targets.UnaryClient, enablePartialResponse bool) func(*http.Request) (interface{}, []error, *api.ApiError, func()) {
  1043  	ps := storepb.PartialResponseStrategy_ABORT
  1044  	if enablePartialResponse {
  1045  		ps = storepb.PartialResponseStrategy_WARN
  1046  	}
  1047  
  1048  	return func(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
  1049  		stateParam := r.URL.Query().Get("state")
  1050  		state, ok := targetspb.TargetsRequest_State_value[strings.ToUpper(stateParam)]
  1051  		if !ok {
  1052  			if stateParam != "" {
  1053  				return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("invalid targets parameter state='%v'", stateParam)}, func() {}
  1054  			}
  1055  			state = int32(targetspb.TargetsRequest_ANY)
  1056  		}
  1057  
  1058  		req := &targetspb.TargetsRequest{
  1059  			State:                   targetspb.TargetsRequest_State(state),
  1060  			PartialResponseStrategy: ps,
  1061  		}
  1062  
  1063  		t, warnings, err := client.Targets(r.Context(), req)
  1064  		if err != nil {
  1065  			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving targets")}, func() {}
  1066  		}
  1067  
  1068  		return t, warnings, nil, func() {}
  1069  	}
  1070  }
  1071  
  1072  // NewAlertsHandler created handler compatible with HTTP /api/v1/alerts https://prometheus.io/docs/prometheus/latest/querying/api/#alerts
  1073  // which uses gRPC Unary Rules API (Rules API works for both /alerts and /rules).
  1074  func NewAlertsHandler(client rules.UnaryClient, enablePartialResponse bool) func(*http.Request) (interface{}, []error, *api.ApiError, func()) {
  1075  	ps := storepb.PartialResponseStrategy_ABORT
  1076  	if enablePartialResponse {
  1077  		ps = storepb.PartialResponseStrategy_WARN
  1078  	}
  1079  
  1080  	return func(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
  1081  		span, ctx := tracing.StartSpan(r.Context(), "receive_http_request")
  1082  		defer span.Finish()
  1083  
  1084  		var (
  1085  			groups   *rulespb.RuleGroups
  1086  			warnings storage.Warnings
  1087  			err      error
  1088  		)
  1089  
  1090  		// TODO(bwplotka): Allow exactly the same functionality as query API: passing replica, dedup and partial response as HTTP params as well.
  1091  		req := &rulespb.RulesRequest{
  1092  			Type:                    rulespb.RulesRequest_ALERT,
  1093  			PartialResponseStrategy: ps,
  1094  		}
  1095  		tracing.DoInSpan(ctx, "retrieve_rules", func(ctx context.Context) {
  1096  			groups, warnings, err = client.Rules(ctx, req)
  1097  		})
  1098  		if err != nil {
  1099  			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Errorf("error retrieving rules: %v", err)}, func() {}
  1100  		}
  1101  
  1102  		var resp struct {
  1103  			Alerts []*rulespb.AlertInstance `json:"alerts"`
  1104  		}
  1105  		for _, g := range groups.Groups {
  1106  			for _, r := range g.Rules {
  1107  				a := r.GetAlert()
  1108  				if a == nil {
  1109  					continue
  1110  				}
  1111  				resp.Alerts = append(resp.Alerts, a.Alerts...)
  1112  			}
  1113  		}
  1114  		return resp, warnings, nil, func() {}
  1115  	}
  1116  }
  1117  
  1118  // NewRulesHandler created handler compatible with HTTP /api/v1/rules https://prometheus.io/docs/prometheus/latest/querying/api/#rules
  1119  // which uses gRPC Unary Rules API.
  1120  func NewRulesHandler(client rules.UnaryClient, enablePartialResponse bool) func(*http.Request) (interface{}, []error, *api.ApiError, func()) {
  1121  	ps := storepb.PartialResponseStrategy_ABORT
  1122  	if enablePartialResponse {
  1123  		ps = storepb.PartialResponseStrategy_WARN
  1124  	}
  1125  
  1126  	return func(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
  1127  		span, ctx := tracing.StartSpan(r.Context(), "receive_http_request")
  1128  		defer span.Finish()
  1129  
  1130  		var (
  1131  			groups   *rulespb.RuleGroups
  1132  			warnings storage.Warnings
  1133  			err      error
  1134  		)
  1135  
  1136  		typeParam := r.URL.Query().Get("type")
  1137  		typ, ok := rulespb.RulesRequest_Type_value[strings.ToUpper(typeParam)]
  1138  		if !ok {
  1139  			if typeParam != "" {
  1140  				return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("invalid rules parameter type='%v'", typeParam)}, func() {}
  1141  			}
  1142  			typ = int32(rulespb.RulesRequest_ALL)
  1143  		}
  1144  
  1145  		if err := r.ParseForm(); err != nil {
  1146  			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Errorf("error parsing request form='%v'", MatcherParam)}, func() {}
  1147  		}
  1148  
  1149  		// TODO(bwplotka): Allow exactly the same functionality as query API: passing replica, dedup and partial response as HTTP params as well.
  1150  		req := &rulespb.RulesRequest{
  1151  			Type:                    rulespb.RulesRequest_Type(typ),
  1152  			PartialResponseStrategy: ps,
  1153  			MatcherString:           r.Form[MatcherParam],
  1154  		}
  1155  		tracing.DoInSpan(ctx, "retrieve_rules", func(ctx context.Context) {
  1156  			groups, warnings, err = client.Rules(ctx, req)
  1157  		})
  1158  		if err != nil {
  1159  			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Errorf("error retrieving rules: %v", err)}, func() {}
  1160  		}
  1161  		return groups, warnings, nil, func() {}
  1162  	}
  1163  }
  1164  
  1165  // NewExemplarsHandler creates handler compatible with HTTP /api/v1/query_exemplars https://prometheus.io/docs/prometheus/latest/querying/api/#querying-exemplars
  1166  // which uses gRPC Unary Exemplars API.
  1167  func NewExemplarsHandler(client exemplars.UnaryClient, enablePartialResponse bool) func(*http.Request) (interface{}, []error, *api.ApiError, func()) {
  1168  	ps := storepb.PartialResponseStrategy_ABORT
  1169  	if enablePartialResponse {
  1170  		ps = storepb.PartialResponseStrategy_WARN
  1171  	}
  1172  
  1173  	return func(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
  1174  		span, ctx := tracing.StartSpan(r.Context(), "exemplar_query_request")
  1175  		defer span.Finish()
  1176  
  1177  		var (
  1178  			data     []*exemplarspb.ExemplarData
  1179  			warnings storage.Warnings
  1180  			err      error
  1181  		)
  1182  
  1183  		start, err := parseTimeParam(r, "start", infMinTime)
  1184  		if err != nil {
  1185  			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
  1186  		}
  1187  		end, err := parseTimeParam(r, "end", infMaxTime)
  1188  		if err != nil {
  1189  			return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
  1190  		}
  1191  
  1192  		req := &exemplarspb.ExemplarsRequest{
  1193  			Start:                   timestamp.FromTime(start),
  1194  			End:                     timestamp.FromTime(end),
  1195  			Query:                   r.FormValue("query"),
  1196  			PartialResponseStrategy: ps,
  1197  		}
  1198  
  1199  		tracing.DoInSpan(ctx, "retrieve_exemplars", func(ctx context.Context) {
  1200  			data, warnings, err = client.Exemplars(ctx, req)
  1201  		})
  1202  
  1203  		if err != nil {
  1204  			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving exemplars")}, func() {}
  1205  		}
  1206  		return data, warnings, nil, func() {}
  1207  	}
  1208  }
  1209  
  1210  var (
  1211  	infMinTime = time.Unix(math.MinInt64/1000+62135596801, 0)
  1212  	infMaxTime = time.Unix(math.MaxInt64/1000-62135596801, 999999999)
  1213  )
  1214  
  1215  func parseMetadataTimeRange(r *http.Request, defaultMetadataTimeRange time.Duration) (time.Time, time.Time, error) {
  1216  	// If start and end time not specified as query parameter, we get the range from the beginning of time by default.
  1217  	var defaultStartTime, defaultEndTime time.Time
  1218  	if defaultMetadataTimeRange == 0 {
  1219  		defaultStartTime = infMinTime
  1220  		defaultEndTime = infMaxTime
  1221  	} else {
  1222  		now := time.Now()
  1223  		defaultStartTime = now.Add(-defaultMetadataTimeRange)
  1224  		defaultEndTime = now
  1225  	}
  1226  
  1227  	start, err := parseTimeParam(r, "start", defaultStartTime)
  1228  	if err != nil {
  1229  		return time.Time{}, time.Time{}, &api.ApiError{Typ: api.ErrorBadData, Err: err}
  1230  	}
  1231  	end, err := parseTimeParam(r, "end", defaultEndTime)
  1232  	if err != nil {
  1233  		return time.Time{}, time.Time{}, &api.ApiError{Typ: api.ErrorBadData, Err: err}
  1234  	}
  1235  	if end.Before(start) {
  1236  		return time.Time{}, time.Time{}, &api.ApiError{
  1237  			Typ: api.ErrorBadData,
  1238  			Err: errors.New("end timestamp must not be before start time"),
  1239  		}
  1240  	}
  1241  
  1242  	return start, end, nil
  1243  }
  1244  
  1245  func parseTimeParam(r *http.Request, paramName string, defaultValue time.Time) (time.Time, error) {
  1246  	val := r.FormValue(paramName)
  1247  	if val == "" {
  1248  		return defaultValue, nil
  1249  	}
  1250  	result, err := parseTime(val)
  1251  	if err != nil {
  1252  		return time.Time{}, errors.Wrapf(err, "Invalid time value for '%s'", paramName)
  1253  	}
  1254  	return result, nil
  1255  }
  1256  
  1257  func parseTime(s string) (time.Time, error) {
  1258  	if t, err := strconv.ParseFloat(s, 64); err == nil {
  1259  		s, ns := math.Modf(t)
  1260  		ns = math.Round(ns*1000) / 1000
  1261  		return time.Unix(int64(s), int64(ns*float64(time.Second))), nil
  1262  	}
  1263  	if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
  1264  		return t, nil
  1265  	}
  1266  	return time.Time{}, errors.Errorf("cannot parse %q to a valid timestamp", s)
  1267  }
  1268  
  1269  func parseDuration(s string) (time.Duration, error) {
  1270  	if d, err := strconv.ParseFloat(s, 64); err == nil {
  1271  		ts := d * float64(time.Second)
  1272  		if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) {
  1273  			return 0, errors.Errorf("cannot parse %q to a valid duration. It overflows int64", s)
  1274  		}
  1275  		return time.Duration(ts), nil
  1276  	}
  1277  	if d, err := model.ParseDuration(s); err == nil {
  1278  		return time.Duration(d), nil
  1279  	}
  1280  	return 0, errors.Errorf("cannot parse %q to a valid duration", s)
  1281  }
  1282  
  1283  // NewMetricMetadataHandler creates handler compatible with HTTP /api/v1/metadata https://prometheus.io/docs/prometheus/latest/querying/api/#querying-metric-metadata
  1284  // which uses gRPC Unary Metadata API.
  1285  func NewMetricMetadataHandler(client metadata.UnaryClient, enablePartialResponse bool) func(*http.Request) (interface{}, []error, *api.ApiError, func()) {
  1286  	ps := storepb.PartialResponseStrategy_ABORT
  1287  	if enablePartialResponse {
  1288  		ps = storepb.PartialResponseStrategy_WARN
  1289  	}
  1290  
  1291  	return func(r *http.Request) (interface{}, []error, *api.ApiError, func()) {
  1292  		span, ctx := tracing.StartSpan(r.Context(), "metadata_http_request")
  1293  		defer span.Finish()
  1294  
  1295  		var (
  1296  			t        map[string][]metadatapb.Meta
  1297  			warnings storage.Warnings
  1298  			err      error
  1299  		)
  1300  
  1301  		req := &metadatapb.MetricMetadataRequest{
  1302  			// By default we use -1, which means no limit.
  1303  			Limit:                   -1,
  1304  			Metric:                  r.URL.Query().Get("metric"),
  1305  			PartialResponseStrategy: ps,
  1306  		}
  1307  
  1308  		limitStr := r.URL.Query().Get("limit")
  1309  		if limitStr != "" {
  1310  			limit, err := strconv.ParseInt(limitStr, 10, 32)
  1311  			if err != nil {
  1312  				return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf("invalid metric metadata limit='%v'", limit)}, func() {}
  1313  			}
  1314  			req.Limit = int32(limit)
  1315  		}
  1316  
  1317  		tracing.DoInSpan(ctx, "retrieve_metadata", func(ctx context.Context) {
  1318  			t, warnings, err = client.MetricMetadata(ctx, req)
  1319  		})
  1320  		if err != nil {
  1321  			return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving metadata")}, func() {}
  1322  		}
  1323  
  1324  		return t, warnings, nil, func() {}
  1325  	}
  1326  }