mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 13:44:05 -08:00
8fdfa8abea
i) Uses the more idiomatic Wrap and Wrapf methods for creating nested errors. ii) Fixes some incorrect usages of fmt.Errorf where the error messages don't have any formatting directives. iii) Does away with the use of fmt package for errors in favour of pkg/errors Signed-off-by: tariqibrahim <tariq181290@gmail.com>
377 lines
9.3 KiB
Go
377 lines
9.3 KiB
Go
// Copyright 2017 The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package remote
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"sort"
|
|
|
|
"github.com/gogo/protobuf/proto"
|
|
"github.com/golang/snappy"
|
|
"github.com/pkg/errors"
|
|
"github.com/prometheus/common/model"
|
|
|
|
"github.com/prometheus/prometheus/pkg/labels"
|
|
"github.com/prometheus/prometheus/prompb"
|
|
"github.com/prometheus/prometheus/storage"
|
|
)
|
|
|
|
// decodeReadLimit is the maximum size of a read request body in bytes.
|
|
const decodeReadLimit = 32 * 1024 * 1024
|
|
|
|
type HTTPError struct {
|
|
msg string
|
|
status int
|
|
}
|
|
|
|
func (e HTTPError) Error() string {
|
|
return e.msg
|
|
}
|
|
|
|
func (e HTTPError) Status() int {
|
|
return e.status
|
|
}
|
|
|
|
// DecodeReadRequest reads a remote.Request from a http.Request.
|
|
func DecodeReadRequest(r *http.Request) (*prompb.ReadRequest, error) {
|
|
compressed, err := ioutil.ReadAll(io.LimitReader(r.Body, decodeReadLimit))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
reqBuf, err := snappy.Decode(nil, compressed)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var req prompb.ReadRequest
|
|
if err := proto.Unmarshal(reqBuf, &req); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &req, nil
|
|
}
|
|
|
|
// EncodeReadResponse writes a remote.Response to a http.ResponseWriter.
|
|
func EncodeReadResponse(resp *prompb.ReadResponse, w http.ResponseWriter) error {
|
|
data, err := proto.Marshal(resp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/x-protobuf")
|
|
w.Header().Set("Content-Encoding", "snappy")
|
|
|
|
compressed := snappy.Encode(nil, data)
|
|
_, err = w.Write(compressed)
|
|
return err
|
|
}
|
|
|
|
// ToQuery builds a Query proto.
|
|
func ToQuery(from, to int64, matchers []*labels.Matcher, p *storage.SelectParams) (*prompb.Query, error) {
|
|
ms, err := toLabelMatchers(matchers)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var rp *prompb.ReadHints
|
|
if p != nil {
|
|
rp = &prompb.ReadHints{
|
|
StepMs: p.Step,
|
|
Func: p.Func,
|
|
StartMs: p.Start,
|
|
EndMs: p.End,
|
|
}
|
|
}
|
|
|
|
return &prompb.Query{
|
|
StartTimestampMs: from,
|
|
EndTimestampMs: to,
|
|
Matchers: ms,
|
|
Hints: rp,
|
|
}, nil
|
|
}
|
|
|
|
// FromQuery unpacks a Query proto.
|
|
func FromQuery(req *prompb.Query) (int64, int64, []*labels.Matcher, *storage.SelectParams, error) {
|
|
matchers, err := fromLabelMatchers(req.Matchers)
|
|
if err != nil {
|
|
return 0, 0, nil, nil, err
|
|
}
|
|
var selectParams *storage.SelectParams
|
|
if req.Hints != nil {
|
|
selectParams = &storage.SelectParams{
|
|
Start: req.Hints.StartMs,
|
|
End: req.Hints.EndMs,
|
|
Step: req.Hints.StepMs,
|
|
Func: req.Hints.Func,
|
|
}
|
|
}
|
|
|
|
return req.StartTimestampMs, req.EndTimestampMs, matchers, selectParams, nil
|
|
}
|
|
|
|
// ToQueryResult builds a QueryResult proto.
|
|
func ToQueryResult(ss storage.SeriesSet, sampleLimit int) (*prompb.QueryResult, error) {
|
|
numSamples := 0
|
|
resp := &prompb.QueryResult{}
|
|
for ss.Next() {
|
|
series := ss.At()
|
|
iter := series.Iterator()
|
|
samples := []prompb.Sample{}
|
|
|
|
for iter.Next() {
|
|
numSamples++
|
|
if sampleLimit > 0 && numSamples > sampleLimit {
|
|
return nil, HTTPError{
|
|
msg: fmt.Sprintf("exceeded sample limit (%d)", sampleLimit),
|
|
status: http.StatusBadRequest,
|
|
}
|
|
}
|
|
ts, val := iter.At()
|
|
samples = append(samples, prompb.Sample{
|
|
Timestamp: ts,
|
|
Value: val,
|
|
})
|
|
}
|
|
if err := iter.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resp.Timeseries = append(resp.Timeseries, &prompb.TimeSeries{
|
|
Labels: labelsToLabelsProto(series.Labels()),
|
|
Samples: samples,
|
|
})
|
|
}
|
|
if err := ss.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return resp, nil
|
|
}
|
|
|
|
// FromQueryResult unpacks a QueryResult proto.
|
|
func FromQueryResult(res *prompb.QueryResult) storage.SeriesSet {
|
|
series := make([]storage.Series, 0, len(res.Timeseries))
|
|
for _, ts := range res.Timeseries {
|
|
labels := labelProtosToLabels(ts.Labels)
|
|
if err := validateLabelsAndMetricName(labels); err != nil {
|
|
return errSeriesSet{err: err}
|
|
}
|
|
|
|
series = append(series, &concreteSeries{
|
|
labels: labels,
|
|
samples: ts.Samples,
|
|
})
|
|
}
|
|
sort.Sort(byLabel(series))
|
|
return &concreteSeriesSet{
|
|
series: series,
|
|
}
|
|
}
|
|
|
|
type byLabel []storage.Series
|
|
|
|
func (a byLabel) Len() int { return len(a) }
|
|
func (a byLabel) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a byLabel) Less(i, j int) bool { return labels.Compare(a[i].Labels(), a[j].Labels()) < 0 }
|
|
|
|
// errSeriesSet implements storage.SeriesSet, just returning an error.
|
|
type errSeriesSet struct {
|
|
err error
|
|
}
|
|
|
|
func (errSeriesSet) Next() bool {
|
|
return false
|
|
}
|
|
|
|
func (errSeriesSet) At() storage.Series {
|
|
return nil
|
|
}
|
|
|
|
func (e errSeriesSet) Err() error {
|
|
return e.err
|
|
}
|
|
|
|
// concreteSeriesSet implements storage.SeriesSet.
|
|
type concreteSeriesSet struct {
|
|
cur int
|
|
series []storage.Series
|
|
}
|
|
|
|
func (c *concreteSeriesSet) Next() bool {
|
|
c.cur++
|
|
return c.cur-1 < len(c.series)
|
|
}
|
|
|
|
func (c *concreteSeriesSet) At() storage.Series {
|
|
return c.series[c.cur-1]
|
|
}
|
|
|
|
func (c *concreteSeriesSet) Err() error {
|
|
return nil
|
|
}
|
|
|
|
// concreteSeries implements storage.Series.
|
|
type concreteSeries struct {
|
|
labels labels.Labels
|
|
samples []prompb.Sample
|
|
}
|
|
|
|
func (c *concreteSeries) Labels() labels.Labels {
|
|
return labels.New(c.labels...)
|
|
}
|
|
|
|
func (c *concreteSeries) Iterator() storage.SeriesIterator {
|
|
return newConcreteSeriersIterator(c)
|
|
}
|
|
|
|
// concreteSeriesIterator implements storage.SeriesIterator.
|
|
type concreteSeriesIterator struct {
|
|
cur int
|
|
series *concreteSeries
|
|
}
|
|
|
|
func newConcreteSeriersIterator(series *concreteSeries) storage.SeriesIterator {
|
|
return &concreteSeriesIterator{
|
|
cur: -1,
|
|
series: series,
|
|
}
|
|
}
|
|
|
|
// Seek implements storage.SeriesIterator.
|
|
func (c *concreteSeriesIterator) Seek(t int64) bool {
|
|
c.cur = sort.Search(len(c.series.samples), func(n int) bool {
|
|
return c.series.samples[n].Timestamp >= t
|
|
})
|
|
return c.cur < len(c.series.samples)
|
|
}
|
|
|
|
// At implements storage.SeriesIterator.
|
|
func (c *concreteSeriesIterator) At() (t int64, v float64) {
|
|
s := c.series.samples[c.cur]
|
|
return s.Timestamp, s.Value
|
|
}
|
|
|
|
// Next implements storage.SeriesIterator.
|
|
func (c *concreteSeriesIterator) Next() bool {
|
|
c.cur++
|
|
return c.cur < len(c.series.samples)
|
|
}
|
|
|
|
// Err implements storage.SeriesIterator.
|
|
func (c *concreteSeriesIterator) Err() error {
|
|
return nil
|
|
}
|
|
|
|
// validateLabelsAndMetricName validates the label names/values and metric names returned from remote read.
|
|
func validateLabelsAndMetricName(ls labels.Labels) error {
|
|
for _, l := range ls {
|
|
if l.Name == labels.MetricName && !model.IsValidMetricName(model.LabelValue(l.Value)) {
|
|
return errors.Errorf("invalid metric name: %v", l.Value)
|
|
}
|
|
if !model.LabelName(l.Name).IsValid() {
|
|
return errors.Errorf("invalid label name: %v", l.Name)
|
|
}
|
|
if !model.LabelValue(l.Value).IsValid() {
|
|
return errors.Errorf("invalid label value: %v", l.Value)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func toLabelMatchers(matchers []*labels.Matcher) ([]*prompb.LabelMatcher, error) {
|
|
pbMatchers := make([]*prompb.LabelMatcher, 0, len(matchers))
|
|
for _, m := range matchers {
|
|
var mType prompb.LabelMatcher_Type
|
|
switch m.Type {
|
|
case labels.MatchEqual:
|
|
mType = prompb.LabelMatcher_EQ
|
|
case labels.MatchNotEqual:
|
|
mType = prompb.LabelMatcher_NEQ
|
|
case labels.MatchRegexp:
|
|
mType = prompb.LabelMatcher_RE
|
|
case labels.MatchNotRegexp:
|
|
mType = prompb.LabelMatcher_NRE
|
|
default:
|
|
return nil, errors.New("invalid matcher type")
|
|
}
|
|
pbMatchers = append(pbMatchers, &prompb.LabelMatcher{
|
|
Type: mType,
|
|
Name: m.Name,
|
|
Value: m.Value,
|
|
})
|
|
}
|
|
return pbMatchers, nil
|
|
}
|
|
|
|
func fromLabelMatchers(matchers []*prompb.LabelMatcher) ([]*labels.Matcher, error) {
|
|
result := make([]*labels.Matcher, 0, len(matchers))
|
|
for _, matcher := range matchers {
|
|
var mtype labels.MatchType
|
|
switch matcher.Type {
|
|
case prompb.LabelMatcher_EQ:
|
|
mtype = labels.MatchEqual
|
|
case prompb.LabelMatcher_NEQ:
|
|
mtype = labels.MatchNotEqual
|
|
case prompb.LabelMatcher_RE:
|
|
mtype = labels.MatchRegexp
|
|
case prompb.LabelMatcher_NRE:
|
|
mtype = labels.MatchNotRegexp
|
|
default:
|
|
return nil, errors.New("invalid matcher type")
|
|
}
|
|
matcher, err := labels.NewMatcher(mtype, matcher.Name, matcher.Value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result = append(result, matcher)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// LabelProtosToMetric unpack a []*prompb.Label to a model.Metric
|
|
func LabelProtosToMetric(labelPairs []*prompb.Label) model.Metric {
|
|
metric := make(model.Metric, len(labelPairs))
|
|
for _, l := range labelPairs {
|
|
metric[model.LabelName(l.Name)] = model.LabelValue(l.Value)
|
|
}
|
|
return metric
|
|
}
|
|
|
|
func labelProtosToLabels(labelPairs []prompb.Label) labels.Labels {
|
|
result := make(labels.Labels, 0, len(labelPairs))
|
|
for _, l := range labelPairs {
|
|
result = append(result, labels.Label{
|
|
Name: l.Name,
|
|
Value: l.Value,
|
|
})
|
|
}
|
|
sort.Sort(result)
|
|
return result
|
|
}
|
|
|
|
func labelsToLabelsProto(labels labels.Labels) []prompb.Label {
|
|
result := make([]prompb.Label, 0, len(labels))
|
|
for _, l := range labels {
|
|
result = append(result, prompb.Label{
|
|
Name: interner.intern(l.Name),
|
|
Value: interner.intern(l.Value),
|
|
})
|
|
}
|
|
return result
|
|
}
|