mirror of
https://github.com/prometheus/prometheus.git
synced 2025-01-11 13:57:36 -08:00
Merge remote-tracking branch 'upstream/main' into sparsehistogram
Signed-off-by: Ganesh Vernekar <ganeshvern@gmail.com>
This commit is contained in:
commit
e3719d670b
|
@ -44,7 +44,7 @@ Release cadence of first pre-releases being cut is 6 weeks.
|
|||
| v2.37 LTS | 2022-06-29 | Julien Pivotto (GitHub: @roidelapluie) |
|
||||
| v2.38 | 2022-08-10 | Julius Volz (GitHub: @juliusv) |
|
||||
| v2.39 | 2022-09-21 | Ganesh Vernekar (GitHub: @codesome) |
|
||||
| v2.40 | 2022-11-02 | **searching for volunteer** |
|
||||
| v2.40 | 2022-11-02 | Ganesh Vernekar (GitHub: @codesome) |
|
||||
| v2.41 | 2022-12-14 | **searching for volunteer** |
|
||||
|
||||
If you are interested in volunteering please create a pull request against the [prometheus/prometheus](https://github.com/prometheus/prometheus) repository and propose yourself for the release series of your choice.
|
||||
|
|
|
@ -60,6 +60,8 @@ const (
|
|||
datacenterLabel = model.MetaLabelPrefix + "consul_dc"
|
||||
// namespaceLabel is the name of the label containing the namespace (Consul Enterprise only).
|
||||
namespaceLabel = model.MetaLabelPrefix + "consul_namespace"
|
||||
// partitionLabel is the name of the label containing the Admin Partition (Consul Enterprise only).
|
||||
partitionLabel = model.MetaLabelPrefix + "consul_partition"
|
||||
// taggedAddressesLabel is the prefix for the labels mapping to a target's tagged addresses.
|
||||
taggedAddressesLabel = model.MetaLabelPrefix + "consul_tagged_address_"
|
||||
// serviceIDLabel is the name of the label containing the service ID.
|
||||
|
@ -112,6 +114,7 @@ type SDConfig struct {
|
|||
Token config.Secret `yaml:"token,omitempty"`
|
||||
Datacenter string `yaml:"datacenter,omitempty"`
|
||||
Namespace string `yaml:"namespace,omitempty"`
|
||||
Partition string `yaml:"partition,omitempty"`
|
||||
TagSeparator string `yaml:"tag_separator,omitempty"`
|
||||
Scheme string `yaml:"scheme,omitempty"`
|
||||
Username string `yaml:"username,omitempty"`
|
||||
|
@ -183,6 +186,7 @@ type Discovery struct {
|
|||
client *consul.Client
|
||||
clientDatacenter string
|
||||
clientNamespace string
|
||||
clientPartition string
|
||||
tagSeparator string
|
||||
watchedServices []string // Set of services which will be discovered.
|
||||
watchedTags []string // Tags used to filter instances of a service.
|
||||
|
@ -210,6 +214,7 @@ func NewDiscovery(conf *SDConfig, logger log.Logger) (*Discovery, error) {
|
|||
Scheme: conf.Scheme,
|
||||
Datacenter: conf.Datacenter,
|
||||
Namespace: conf.Namespace,
|
||||
Partition: conf.Partition,
|
||||
Token: string(conf.Token),
|
||||
HttpClient: wrapper,
|
||||
}
|
||||
|
@ -227,6 +232,7 @@ func NewDiscovery(conf *SDConfig, logger log.Logger) (*Discovery, error) {
|
|||
refreshInterval: time.Duration(conf.RefreshInterval),
|
||||
clientDatacenter: conf.Datacenter,
|
||||
clientNamespace: conf.Namespace,
|
||||
clientPartition: conf.Partition,
|
||||
finalizer: wrapper.CloseIdleConnections,
|
||||
logger: logger,
|
||||
}
|
||||
|
@ -547,6 +553,7 @@ func (srv *consulService) watch(ctx context.Context, ch chan<- []*targetgroup.Gr
|
|||
addressLabel: model.LabelValue(serviceNode.Node.Address),
|
||||
nodeLabel: model.LabelValue(serviceNode.Node.Node),
|
||||
namespaceLabel: model.LabelValue(serviceNode.Service.Namespace),
|
||||
partitionLabel: model.LabelValue(serviceNode.Service.Partition),
|
||||
tagsLabel: model.LabelValue(tags),
|
||||
serviceAddressLabel: model.LabelValue(serviceNode.Service.Address),
|
||||
servicePortLabel: model.LabelValue(strconv.Itoa(serviceNode.Service.Port)),
|
||||
|
|
|
@ -518,6 +518,7 @@ The following meta labels are available on targets during [relabeling](#relabel_
|
|||
* `__meta_consul_address`: the address of the target
|
||||
* `__meta_consul_dc`: the datacenter name for the target
|
||||
* `__meta_consul_health`: the health status of the service
|
||||
* `__meta_consul_partition`: the admin partition name where the service is registered
|
||||
* `__meta_consul_metadata_<key>`: each node metadata key value of the target
|
||||
* `__meta_consul_node`: the node name defined for the target
|
||||
* `__meta_consul_service_address`: the service address of the target
|
||||
|
@ -536,6 +537,8 @@ The following meta labels are available on targets during [relabeling](#relabel_
|
|||
[ datacenter: <string> ]
|
||||
# Namespaces are only supported in Consul Enterprise.
|
||||
[ namespace: <string> ]
|
||||
# Admin Partitions are only supported in Consul Enterprise.
|
||||
[ partition: <string> ]
|
||||
[ scheme: <string> | default = "http" ]
|
||||
# The username and password fields are deprecated in favor of the basic_auth configuration.
|
||||
[ username: <string> ]
|
||||
|
@ -2266,7 +2269,7 @@ tls_config:
|
|||
### `<serverset_sd_config>`
|
||||
|
||||
Serverset SD configurations allow retrieving scrape targets from [Serversets]
|
||||
(https://github.com/twitter/finagle/tree/master/finagle-serversets) which are
|
||||
(https://github.com/twitter/finagle/tree/develop/finagle-serversets) which are
|
||||
stored in [Zookeeper](https://zookeeper.apache.org/). Serversets are commonly
|
||||
used by [Finagle](https://twitter.github.io/finagle/) and
|
||||
[Aurora](https://aurora.apache.org/).
|
||||
|
|
6
go.mod
6
go.mod
|
@ -11,7 +11,7 @@ require (
|
|||
github.com/cespare/xxhash/v2 v2.1.2
|
||||
github.com/dennwc/varint v1.0.0
|
||||
github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245
|
||||
github.com/digitalocean/godo v1.84.1
|
||||
github.com/digitalocean/godo v1.86.0
|
||||
github.com/docker/docker v20.10.18+incompatible
|
||||
github.com/edsrzf/mmap-go v1.1.0
|
||||
github.com/envoyproxy/go-control-plane v0.10.3
|
||||
|
@ -60,13 +60,13 @@ require (
|
|||
go.uber.org/atomic v1.10.0
|
||||
go.uber.org/automaxprocs v1.5.1
|
||||
go.uber.org/goleak v1.2.0
|
||||
golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9
|
||||
golang.org/x/net v0.0.0-20220921155015-db77216a4ee9
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
|
||||
golang.org/x/sync v0.0.0-20220907140024-f12130a52804
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
|
||||
golang.org/x/time v0.0.0-20220920022843-2ce7c2934d45
|
||||
golang.org/x/tools v0.1.12
|
||||
google.golang.org/api v0.96.0
|
||||
google.golang.org/api v0.98.0
|
||||
google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006
|
||||
google.golang.org/grpc v1.49.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
|
|
12
go.sum
12
go.sum
|
@ -175,8 +175,8 @@ github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgz
|
|||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245 h1:9cOfvEwjQxdwKuNDTQSaMKNRvwKwgZG+U4HrjeRKHso=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20200911182023-62edffca9245/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/digitalocean/godo v1.84.1 h1:VgPsuxhrO9pUygvij6qOhqXfAkxAsDZYRpmjSDMEaHo=
|
||||
github.com/digitalocean/godo v1.84.1/go.mod h1:BPCqvwbjbGqxuUnIKB4EvS/AX7IDnNmt5fwvIkWo+ew=
|
||||
github.com/digitalocean/godo v1.86.0 h1:GKB2HS+6lnYPn+9XLLsIVBWk3xk7v568EJnmdHuyhKA=
|
||||
github.com/digitalocean/godo v1.86.0/go.mod h1:jELt1jkHVifd0rKaY0pt/m1QxGzbkkvoVVrDkR15/5A=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
|
@ -1011,8 +1011,8 @@ golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug
|
|||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220907135653-1e95f45603a7/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9 h1:asZqf0wXastQr+DudYagQS8uBO8bHKeYD1vbAvGmFL8=
|
||||
golang.org/x/net v0.0.0-20220920203100-d0c6ba3f52d9/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220921155015-db77216a4ee9 h1:SdDGdqRuKrF2R4XGcnPzcvZ63c/55GvhoHUus0o+BNI=
|
||||
golang.org/x/net v0.0.0-20220921155015-db77216a4ee9/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -1281,8 +1281,8 @@ google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69
|
|||
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
||||
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
|
||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||
google.golang.org/api v0.96.0 h1:F60cuQPJq7K7FzsxMYHAUJSiXh2oKctHxBMbDygxhfM=
|
||||
google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/api v0.98.0 h1:yxZrcxXESimy6r6mdL5Q6EnZwmewDJK2dVg3g75s5Dg=
|
||||
google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
|
|
@ -553,12 +553,12 @@ func (api *API) queryExemplars(r *http.Request) apiFuncResult {
|
|||
ctx := r.Context()
|
||||
eq, err := api.ExemplarQueryable.ExemplarQuerier(ctx)
|
||||
if err != nil {
|
||||
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil}
|
||||
return apiFuncResult{nil, returnAPIError(err), nil, nil}
|
||||
}
|
||||
|
||||
res, err := eq.Select(timestamp.FromTime(start), timestamp.FromTime(end), selectors...)
|
||||
if err != nil {
|
||||
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil}
|
||||
return apiFuncResult{nil, returnAPIError(err), nil, nil}
|
||||
}
|
||||
|
||||
return apiFuncResult{res, nil, nil, nil}
|
||||
|
@ -603,7 +603,7 @@ func (api *API) labelNames(r *http.Request) apiFuncResult {
|
|||
|
||||
q, err := api.Queryable.Querier(r.Context(), timestamp.FromTime(start), timestamp.FromTime(end))
|
||||
if err != nil {
|
||||
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil}
|
||||
return apiFuncResult{nil, returnAPIError(err), nil, nil}
|
||||
}
|
||||
defer q.Close()
|
||||
|
||||
|
@ -617,7 +617,7 @@ func (api *API) labelNames(r *http.Request) apiFuncResult {
|
|||
for _, matchers := range matcherSets {
|
||||
vals, callWarnings, err := q.LabelNames(matchers...)
|
||||
if err != nil {
|
||||
return apiFuncResult{nil, &apiError{errorExec, err}, warnings, nil}
|
||||
return apiFuncResult{nil, returnAPIError(err), warnings, nil}
|
||||
}
|
||||
|
||||
warnings = append(warnings, callWarnings...)
|
||||
|
@ -753,7 +753,7 @@ func (api *API) series(r *http.Request) (result apiFuncResult) {
|
|||
|
||||
q, err := api.Queryable.Querier(r.Context(), timestamp.FromTime(start), timestamp.FromTime(end))
|
||||
if err != nil {
|
||||
return apiFuncResult{nil, &apiError{errorExec, err}, nil, nil}
|
||||
return apiFuncResult{nil, returnAPIError(err), nil, nil}
|
||||
}
|
||||
// From now on, we must only return with a finalizer in the result (to
|
||||
// be called by the caller) or call q.Close ourselves (which is required
|
||||
|
@ -794,7 +794,7 @@ func (api *API) series(r *http.Request) (result apiFuncResult) {
|
|||
|
||||
warnings := set.Warnings()
|
||||
if set.Err() != nil {
|
||||
return apiFuncResult{nil, &apiError{errorExec, set.Err()}, warnings, closer}
|
||||
return apiFuncResult{nil, returnAPIError(set.Err()), warnings, closer}
|
||||
}
|
||||
|
||||
return apiFuncResult{metrics, nil, warnings, closer}
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/prometheus/prometheus/model/histogram"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/prometheus/prometheus/util/stats"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
|
@ -47,7 +48,6 @@ import (
|
|||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/model/textparse"
|
||||
"github.com/prometheus/prometheus/model/timestamp"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"github.com/prometheus/prometheus/promql/parser"
|
||||
"github.com/prometheus/prometheus/rules"
|
||||
|
@ -453,7 +453,13 @@ func TestEndpoints(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestLabelNames(t *testing.T) {
|
||||
type byLabels []labels.Labels
|
||||
|
||||
func (b byLabels) Len() int { return len(b) }
|
||||
func (b byLabels) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
func (b byLabels) Less(i, j int) bool { return labels.Compare(b[i], b[j]) < 0 }
|
||||
|
||||
func TestGetSeries(t *testing.T) {
|
||||
// TestEndpoints doesn't have enough label names to test api.labelNames
|
||||
// endpoint properly. Hence we test it separately.
|
||||
suite, err := promql.NewTest(t, `
|
||||
|
@ -467,7 +473,6 @@ func TestLabelNames(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
defer suite.Close()
|
||||
require.NoError(t, suite.Run())
|
||||
|
||||
api := &API{
|
||||
Queryable: suite.Storage(),
|
||||
}
|
||||
|
@ -488,28 +493,286 @@ func TestLabelNames(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
matchers []string
|
||||
expected []string
|
||||
name string
|
||||
api *API
|
||||
matchers []string
|
||||
expected []labels.Labels
|
||||
expectedErrorType errorType
|
||||
}{
|
||||
{
|
||||
name: "no matchers",
|
||||
expectedErrorType: errorBadData,
|
||||
api: api,
|
||||
},
|
||||
{
|
||||
name: "non empty label matcher",
|
||||
matchers: []string{`{foo=~".+"}`},
|
||||
expected: []labels.Labels{
|
||||
{labels.Label{Name: "__name__", Value: "test_metric2"}, labels.Label{Name: "abc", Value: "qwerty"}, labels.Label{Name: "foo", Value: "baz"}},
|
||||
{labels.Label{Name: "__name__", Value: "test_metric2"}, labels.Label{Name: "foo", Value: "boo"}},
|
||||
{labels.Label{Name: "__name__", Value: "test_metric2"}, labels.Label{Name: "foo", Value: "boo"}, labels.Label{Name: "xyz", Value: "qwerty"}},
|
||||
},
|
||||
api: api,
|
||||
},
|
||||
{
|
||||
name: "exact label matcher",
|
||||
matchers: []string{`{foo="boo"}`},
|
||||
expected: []labels.Labels{
|
||||
{labels.Label{Name: "__name__", Value: "test_metric2"}, labels.Label{Name: "foo", Value: "boo"}},
|
||||
{labels.Label{Name: "__name__", Value: "test_metric2"}, labels.Label{Name: "foo", Value: "boo"}, labels.Label{Name: "xyz", Value: "qwerty"}},
|
||||
},
|
||||
api: api,
|
||||
},
|
||||
{
|
||||
name: "two matchers",
|
||||
matchers: []string{`{foo="boo"}`, `{foo="baz"}`},
|
||||
expected: []labels.Labels{
|
||||
{labels.Label{Name: "__name__", Value: "test_metric2"}, labels.Label{Name: "abc", Value: "qwerty"}, labels.Label{Name: "foo", Value: "baz"}},
|
||||
{labels.Label{Name: "__name__", Value: "test_metric2"}, labels.Label{Name: "foo", Value: "boo"}},
|
||||
{labels.Label{Name: "__name__", Value: "test_metric2"}, labels.Label{Name: "foo", Value: "boo"}, labels.Label{Name: "xyz", Value: "qwerty"}},
|
||||
},
|
||||
api: api,
|
||||
},
|
||||
{
|
||||
name: "exec error type",
|
||||
matchers: []string{`{foo="boo"}`, `{foo="baz"}`},
|
||||
expectedErrorType: errorExec,
|
||||
api: &API{
|
||||
Queryable: errorTestQueryable{err: fmt.Errorf("generic")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storage error type",
|
||||
matchers: []string{`{foo="boo"}`, `{foo="baz"}`},
|
||||
expectedErrorType: errorInternal,
|
||||
api: &API{
|
||||
Queryable: errorTestQueryable{err: promql.ErrStorage{Err: fmt.Errorf("generic")}},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
req, err := request(http.MethodGet, tc.matchers...)
|
||||
require.NoError(t, err)
|
||||
res := tc.api.series(req.WithContext(ctx))
|
||||
assertAPIError(t, res.err, tc.expectedErrorType)
|
||||
if tc.expectedErrorType == errorNone {
|
||||
r := res.data.([]labels.Labels)
|
||||
for _, l := range tc.expected {
|
||||
sort.Sort(l)
|
||||
}
|
||||
for _, l := range r {
|
||||
sort.Sort(l)
|
||||
}
|
||||
sort.Sort(byLabels(tc.expected))
|
||||
sort.Sort(byLabels(r))
|
||||
require.Equal(t, tc.expected, r)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueryExemplars(t *testing.T) {
|
||||
start := time.Unix(0, 0)
|
||||
suite, err := promql.NewTest(t, `
|
||||
load 1m
|
||||
test_metric1{foo="bar"} 0+100x100
|
||||
test_metric1{foo="boo"} 1+0x100
|
||||
test_metric2{foo="boo"} 1+0x100
|
||||
test_metric3{foo="bar", dup="1"} 1+0x100
|
||||
test_metric3{foo="boo", dup="1"} 1+0x100
|
||||
test_metric4{foo="bar", dup="1"} 1+0x100
|
||||
test_metric4{foo="boo", dup="1"} 1+0x100
|
||||
test_metric4{foo="boo"} 1+0x100
|
||||
`)
|
||||
|
||||
require.NoError(t, err)
|
||||
defer suite.Close()
|
||||
require.NoError(t, suite.Run())
|
||||
|
||||
api := &API{
|
||||
Queryable: suite.Storage(),
|
||||
QueryEngine: suite.QueryEngine(),
|
||||
ExemplarQueryable: suite.ExemplarQueryable(),
|
||||
}
|
||||
|
||||
request := func(method string, qs url.Values) (*http.Request, error) {
|
||||
u, err := url.Parse("http://example.com")
|
||||
require.NoError(t, err)
|
||||
u.RawQuery = qs.Encode()
|
||||
r, err := http.NewRequest(method, u.String(), nil)
|
||||
if method == http.MethodPost {
|
||||
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
query url.Values
|
||||
exemplars []exemplar.QueryResult
|
||||
api *API
|
||||
expectedErrorType errorType
|
||||
}{
|
||||
{
|
||||
name: "no error",
|
||||
api: api,
|
||||
query: url.Values{
|
||||
"query": []string{`test_metric3{foo="boo"} - test_metric4{foo="bar"}`},
|
||||
"start": []string{"0"},
|
||||
"end": []string{"4"},
|
||||
},
|
||||
exemplars: []exemplar.QueryResult{
|
||||
{
|
||||
SeriesLabels: labels.FromStrings("__name__", "test_metric3", "foo", "boo", "dup", "1"),
|
||||
Exemplars: []exemplar.Exemplar{
|
||||
{
|
||||
Labels: labels.FromStrings("id", "abc"),
|
||||
Value: 10,
|
||||
Ts: timestamp.FromTime(start.Add(0 * time.Second)),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
SeriesLabels: labels.FromStrings("__name__", "test_metric4", "foo", "bar", "dup", "1"),
|
||||
Exemplars: []exemplar.Exemplar{
|
||||
{
|
||||
Labels: labels.FromStrings("id", "lul"),
|
||||
Value: 10,
|
||||
Ts: timestamp.FromTime(start.Add(3 * time.Second)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return errorExec upon genetic error",
|
||||
expectedErrorType: errorExec,
|
||||
api: &API{
|
||||
ExemplarQueryable: errorTestQueryable{err: fmt.Errorf("generic")},
|
||||
},
|
||||
query: url.Values{
|
||||
"query": []string{`test_metric3{foo="boo"} - test_metric4{foo="bar"}`},
|
||||
"start": []string{"0"},
|
||||
"end": []string{"4"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "should return errorInternal err type is ErrStorage",
|
||||
expectedErrorType: errorInternal,
|
||||
api: &API{
|
||||
ExemplarQueryable: errorTestQueryable{err: promql.ErrStorage{Err: fmt.Errorf("generic")}},
|
||||
},
|
||||
query: url.Values{
|
||||
"query": []string{`test_metric3{foo="boo"} - test_metric4{foo="bar"}`},
|
||||
"start": []string{"0"},
|
||||
"end": []string{"4"},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
es := suite.ExemplarStorage()
|
||||
ctx := context.Background()
|
||||
|
||||
for _, te := range tc.exemplars {
|
||||
for _, e := range te.Exemplars {
|
||||
_, err := es.AppendExemplar(0, te.SeriesLabels, e)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req, err := request(http.MethodGet, tc.query)
|
||||
require.NoError(t, err)
|
||||
res := tc.api.queryExemplars(req.WithContext(ctx))
|
||||
assertAPIError(t, res.err, tc.expectedErrorType)
|
||||
|
||||
if tc.expectedErrorType == errorNone {
|
||||
assertAPIResponse(t, res.data, tc.exemplars)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLabelNames(t *testing.T) {
|
||||
// TestEndpoints doesn't have enough label names to test api.labelNames
|
||||
// endpoint properly. Hence we test it separately.
|
||||
suite, err := promql.NewTest(t, `
|
||||
load 1m
|
||||
test_metric1{foo1="bar", baz="abc"} 0+100x100
|
||||
test_metric1{foo2="boo"} 1+0x100
|
||||
test_metric2{foo="boo"} 1+0x100
|
||||
test_metric2{foo="boo", xyz="qwerty"} 1+0x100
|
||||
test_metric2{foo="baz", abc="qwerty"} 1+0x100
|
||||
`)
|
||||
require.NoError(t, err)
|
||||
defer suite.Close()
|
||||
require.NoError(t, suite.Run())
|
||||
api := &API{
|
||||
Queryable: suite.Storage(),
|
||||
}
|
||||
request := func(method string, matchers ...string) (*http.Request, error) {
|
||||
u, err := url.Parse("http://example.com")
|
||||
require.NoError(t, err)
|
||||
q := u.Query()
|
||||
for _, matcher := range matchers {
|
||||
q.Add("match[]", matcher)
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
r, err := http.NewRequest(method, u.String(), nil)
|
||||
if method == http.MethodPost {
|
||||
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
api *API
|
||||
matchers []string
|
||||
expected []string
|
||||
expectedErrorType errorType
|
||||
}{
|
||||
{
|
||||
name: "no matchers",
|
||||
expected: []string{"__name__", "abc", "baz", "foo", "foo1", "foo2", "xyz"},
|
||||
api: api,
|
||||
},
|
||||
{
|
||||
name: "non empty label matcher",
|
||||
matchers: []string{`{foo=~".+"}`},
|
||||
expected: []string{"__name__", "abc", "foo", "xyz"},
|
||||
api: api,
|
||||
},
|
||||
{
|
||||
name: "exact label matcher",
|
||||
matchers: []string{`{foo="boo"}`},
|
||||
expected: []string{"__name__", "foo", "xyz"},
|
||||
api: api,
|
||||
},
|
||||
{
|
||||
name: "two matchers",
|
||||
matchers: []string{`{foo="boo"}`, `{foo="baz"}`},
|
||||
expected: []string{"__name__", "abc", "foo", "xyz"},
|
||||
api: api,
|
||||
},
|
||||
{
|
||||
name: "exec error type",
|
||||
matchers: []string{`{foo="boo"}`, `{foo="baz"}`},
|
||||
expectedErrorType: errorExec,
|
||||
api: &API{
|
||||
Queryable: errorTestQueryable{err: fmt.Errorf("generic")},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storage error type",
|
||||
matchers: []string{`{foo="boo"}`, `{foo="baz"}`},
|
||||
expectedErrorType: errorInternal,
|
||||
api: &API{
|
||||
Queryable: errorTestQueryable{err: promql.ErrStorage{Err: fmt.Errorf("generic")}},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
@ -517,9 +780,11 @@ func TestLabelNames(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
req, err := request(method, tc.matchers...)
|
||||
require.NoError(t, err)
|
||||
res := api.labelNames(req.WithContext(ctx))
|
||||
assertAPIError(t, res.err, "")
|
||||
assertAPIResponse(t, res.data, tc.expected)
|
||||
res := tc.api.labelNames(req.WithContext(ctx))
|
||||
assertAPIError(t, res.err, tc.expectedErrorType)
|
||||
if tc.expectedErrorType == errorNone {
|
||||
assertAPIResponse(t, res.data, tc.expected)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -141,10 +141,15 @@ func createPrometheusAPI(q storage.SampleAndChunkQueryable) *route.Router {
|
|||
}
|
||||
|
||||
type errorTestQueryable struct {
|
||||
storage.ExemplarQueryable
|
||||
q storage.Querier
|
||||
err error
|
||||
}
|
||||
|
||||
func (t errorTestQueryable) ExemplarQuerier(ctx context.Context) (storage.ExemplarQuerier, error) {
|
||||
return nil, t.err
|
||||
}
|
||||
|
||||
func (t errorTestQueryable) ChunkQuerier(ctx context.Context, mint, maxt int64) (storage.ChunkQuerier, error) {
|
||||
return nil, t.err
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue