mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-01 14:47:38 +00:00
apptest: adds cluster test for multitenant API requests
This commit adds integration test for multitenant via labels feature - https://docs.victoriametrics.com/cluster-victoriametrics/#multitenancy-via-labels It also extends current test models in order to: - accept float timestamps returned from /api/v1/query_range and query api https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/app/vmselect/prometheus/util.qtpl#L43 - accept arbitrary query url params for requests. It simplifies testing for the different VM API extensions --------- Signed-off-by: f41gh7 <nik@victoriametrics.com>
This commit is contained in:
parent
9cfdbc582f
commit
bb99ddf957
7 changed files with 284 additions and 72 deletions
|
@ -3,6 +3,7 @@ package apptest
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -14,9 +15,9 @@ import (
|
||||||
|
|
||||||
// PrometheusQuerier contains methods available to Prometheus-like HTTP API for Querying
|
// PrometheusQuerier contains methods available to Prometheus-like HTTP API for Querying
|
||||||
type PrometheusQuerier interface {
|
type PrometheusQuerier interface {
|
||||||
PrometheusAPIV1Export(t *testing.T, query, start, end string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
PrometheusAPIV1Export(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
||||||
PrometheusAPIV1Query(t *testing.T, query, time, step string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
PrometheusAPIV1Query(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
||||||
PrometheusAPIV1QueryRange(t *testing.T, query, start, end, step string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
PrometheusAPIV1QueryRange(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
||||||
PrometheusAPIV1Series(t *testing.T, matchQuery string, opts QueryOpts) *PrometheusAPIV1SeriesResponse
|
PrometheusAPIV1Series(t *testing.T, matchQuery string, opts QueryOpts) *PrometheusAPIV1SeriesResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +45,41 @@ type PrometheusWriteQuerier interface {
|
||||||
type QueryOpts struct {
|
type QueryOpts struct {
|
||||||
Tenant string
|
Tenant string
|
||||||
Timeout string
|
Timeout string
|
||||||
|
Start string
|
||||||
|
End string
|
||||||
|
Time string
|
||||||
|
Step string
|
||||||
|
ExtraFilters []string
|
||||||
|
ExtraLabels []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qos *QueryOpts) asURLValues() url.Values {
|
||||||
|
uv := make(url.Values)
|
||||||
|
addNonEmpty := func(name string, values ...string) {
|
||||||
|
for _, value := range values {
|
||||||
|
if len(value) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
uv.Add(name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addNonEmpty("start", qos.Start)
|
||||||
|
addNonEmpty("end", qos.End)
|
||||||
|
addNonEmpty("time", qos.Time)
|
||||||
|
addNonEmpty("step", qos.Step)
|
||||||
|
addNonEmpty("timeout", qos.Timeout)
|
||||||
|
addNonEmpty("extra_label", qos.ExtraLabels...)
|
||||||
|
addNonEmpty("extra_filters", qos.ExtraFilters...)
|
||||||
|
|
||||||
|
return uv
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTenant returns tenant with optional default value
|
||||||
|
func (qos *QueryOpts) getTenant() string {
|
||||||
|
if qos.Tenant == "" {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
return qos.Tenant
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrometheusAPIV1QueryResponse is an inmemory representation of the
|
// PrometheusAPIV1QueryResponse is an inmemory representation of the
|
||||||
|
@ -60,7 +96,7 @@ func NewPrometheusAPIV1QueryResponse(t *testing.T, s string) *PrometheusAPIV1Que
|
||||||
|
|
||||||
res := &PrometheusAPIV1QueryResponse{}
|
res := &PrometheusAPIV1QueryResponse{}
|
||||||
if err := json.Unmarshal([]byte(s), res); err != nil {
|
if err := json.Unmarshal([]byte(s), res); err != nil {
|
||||||
t.Fatalf("could not unmarshal query response: %v", err)
|
t.Fatalf("could not unmarshal query response data=\n%s\n: %v", string(s), err)
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -101,7 +137,7 @@ func NewSample(t *testing.T, timeStr string, value float64) *Sample {
|
||||||
// UnmarshalJSON populates the sample fields from a JSON string.
|
// UnmarshalJSON populates the sample fields from a JSON string.
|
||||||
func (s *Sample) UnmarshalJSON(b []byte) error {
|
func (s *Sample) UnmarshalJSON(b []byte) error {
|
||||||
var (
|
var (
|
||||||
ts int64
|
ts float64
|
||||||
v string
|
v string
|
||||||
)
|
)
|
||||||
raw := []any{&ts, &v}
|
raw := []any{&ts, &v}
|
||||||
|
@ -111,7 +147,7 @@ func (s *Sample) UnmarshalJSON(b []byte) error {
|
||||||
if got, want := len(raw), 2; got != want {
|
if got, want := len(raw), 2; got != want {
|
||||||
return fmt.Errorf("unexpected number of fields: got %d, want %d (raw sample: %s)", got, want, string(b))
|
return fmt.Errorf("unexpected number of fields: got %d, want %d (raw sample: %s)", got, want, string(b))
|
||||||
}
|
}
|
||||||
s.Timestamp = ts
|
s.Timestamp = int64(ts)
|
||||||
var err error
|
var err error
|
||||||
s.Value, err = strconv.ParseFloat(v, 64)
|
s.Value, err = strconv.ParseFloat(v, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -135,7 +171,7 @@ func NewPrometheusAPIV1SeriesResponse(t *testing.T, s string) *PrometheusAPIV1Se
|
||||||
|
|
||||||
res := &PrometheusAPIV1SeriesResponse{}
|
res := &PrometheusAPIV1SeriesResponse{}
|
||||||
if err := json.Unmarshal([]byte(s), res); err != nil {
|
if err := json.Unmarshal([]byte(s), res); err != nil {
|
||||||
t.Fatalf("could not unmarshal series response: %v", err)
|
t.Fatalf("could not unmarshal series response data:\n%s\n err: %v", string(s), err)
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,26 +51,25 @@ func TestClusterKeyConceptsQueryData(t *testing.T) {
|
||||||
|
|
||||||
// testClusterKeyConceptsQuery verifies cases from https://docs.victoriametrics.com/keyconcepts/#query-data
|
// testClusterKeyConceptsQuery verifies cases from https://docs.victoriametrics.com/keyconcepts/#query-data
|
||||||
func testKeyConceptsQueryData(t *testing.T, sut apptest.PrometheusWriteQuerier) {
|
func testKeyConceptsQueryData(t *testing.T, sut apptest.PrometheusWriteQuerier) {
|
||||||
opts := apptest.QueryOpts{Timeout: "5s", Tenant: "0"}
|
|
||||||
|
|
||||||
// Insert example data from documentation.
|
// Insert example data from documentation.
|
||||||
sut.PrometheusAPIV1ImportPrometheus(t, docData, opts)
|
sut.PrometheusAPIV1ImportPrometheus(t, docData, apptest.QueryOpts{})
|
||||||
sut.ForceFlush(t)
|
sut.ForceFlush(t)
|
||||||
|
|
||||||
testInstantQuery(t, sut, opts)
|
testInstantQuery(t, sut)
|
||||||
testRangeQuery(t, sut, opts)
|
testRangeQuery(t, sut)
|
||||||
testRangeQueryIsEquivalentToManyInstantQueries(t, sut, opts)
|
testRangeQueryIsEquivalentToManyInstantQueries(t, sut)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testInstantQuery verifies the statements made in the `Instant query` section
|
// testInstantQuery verifies the statements made in the `Instant query` section
|
||||||
// of the VictoriaMetrics documentation. See:
|
// of the VictoriaMetrics documentation. See:
|
||||||
// https://docs.victoriametrics.com/keyconcepts/#instant-query
|
// https://docs.victoriametrics.com/keyconcepts/#instant-query
|
||||||
func testInstantQuery(t *testing.T, q apptest.PrometheusQuerier, opts apptest.QueryOpts) {
|
func testInstantQuery(t *testing.T, q apptest.PrometheusQuerier) {
|
||||||
// Get the value of the foo_bar time series at 2022-05-10T08:03:00Z with the
|
// Get the value of the foo_bar time series at 2022-05-10T08:03:00Z with the
|
||||||
// step of 5m and timeout 5s. There is no sample at exactly this timestamp.
|
// step of 5m and timeout 5s. There is no sample at exactly this timestamp.
|
||||||
// Therefore, VictoriaMetrics will search for the nearest sample within the
|
// Therefore, VictoriaMetrics will search for the nearest sample within the
|
||||||
// [time-5m..time] interval.
|
// [time-5m..time] interval.
|
||||||
got := q.PrometheusAPIV1Query(t, "foo_bar", "2022-05-10T08:03:00.000Z", "5m", opts)
|
got := q.PrometheusAPIV1Query(t, "foo_bar", apptest.QueryOpts{Time: "2022-05-10T08:03:00.000Z", Step: "5m"})
|
||||||
want := apptest.NewPrometheusAPIV1QueryResponse(t, `{"data":{"result":[{"metric":{"__name__":"foo_bar"},"value":[1652169780,"3"]}]}}`)
|
want := apptest.NewPrometheusAPIV1QueryResponse(t, `{"data":{"result":[{"metric":{"__name__":"foo_bar"},"value":[1652169780,"3"]}]}}`)
|
||||||
opt := cmpopts.IgnoreFields(apptest.PrometheusAPIV1QueryResponse{}, "Status", "Data.ResultType")
|
opt := cmpopts.IgnoreFields(apptest.PrometheusAPIV1QueryResponse{}, "Status", "Data.ResultType")
|
||||||
if diff := cmp.Diff(want, got, opt); diff != "" {
|
if diff := cmp.Diff(want, got, opt); diff != "" {
|
||||||
|
@ -82,7 +81,7 @@ func testInstantQuery(t *testing.T, q apptest.PrometheusQuerier, opts apptest.Qu
|
||||||
// Therefore, VictoriaMetrics will search for the nearest sample within the
|
// Therefore, VictoriaMetrics will search for the nearest sample within the
|
||||||
// [time-1m..time] interval. Since the nearest sample is 2m away and the
|
// [time-1m..time] interval. Since the nearest sample is 2m away and the
|
||||||
// step is 1m, then the VictoriaMetrics must return empty response.
|
// step is 1m, then the VictoriaMetrics must return empty response.
|
||||||
got = q.PrometheusAPIV1Query(t, "foo_bar", "2022-05-10T08:18:00.000Z", "1m", opts)
|
got = q.PrometheusAPIV1Query(t, "foo_bar", apptest.QueryOpts{Time: "2022-05-10T08:18:00.000Z", Step: "1m"})
|
||||||
if len(got.Data.Result) > 0 {
|
if len(got.Data.Result) > 0 {
|
||||||
t.Errorf("unexpected response: got non-empty result, want empty result:\n%v", got)
|
t.Errorf("unexpected response: got non-empty result, want empty result:\n%v", got)
|
||||||
}
|
}
|
||||||
|
@ -91,11 +90,11 @@ func testInstantQuery(t *testing.T, q apptest.PrometheusQuerier, opts apptest.Qu
|
||||||
// testRangeQuery verifies the statements made in the `Range query` section of
|
// testRangeQuery verifies the statements made in the `Range query` section of
|
||||||
// the VictoriaMetrics documentation. See:
|
// the VictoriaMetrics documentation. See:
|
||||||
// https://docs.victoriametrics.com/keyconcepts/#range-query
|
// https://docs.victoriametrics.com/keyconcepts/#range-query
|
||||||
func testRangeQuery(t *testing.T, q apptest.PrometheusQuerier, opts apptest.QueryOpts) {
|
func testRangeQuery(t *testing.T, q apptest.PrometheusQuerier) {
|
||||||
f := func(start, end, step string, wantSamples []*apptest.Sample) {
|
f := func(start, end, step string, wantSamples []*apptest.Sample) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
got := q.PrometheusAPIV1QueryRange(t, "foo_bar", start, end, step, opts)
|
got := q.PrometheusAPIV1QueryRange(t, "foo_bar", apptest.QueryOpts{Start: start, End: end, Step: step})
|
||||||
want := apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": [{"metric": {"__name__": "foo_bar"}, "values": []}]}}`)
|
want := apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": [{"metric": {"__name__": "foo_bar"}, "values": []}]}}`)
|
||||||
want.Data.Result[0].Samples = wantSamples
|
want.Data.Result[0].Samples = wantSamples
|
||||||
opt := cmpopts.IgnoreFields(apptest.PrometheusAPIV1QueryResponse{}, "Status", "Data.ResultType")
|
opt := cmpopts.IgnoreFields(apptest.PrometheusAPIV1QueryResponse{}, "Status", "Data.ResultType")
|
||||||
|
@ -162,11 +161,11 @@ func testRangeQuery(t *testing.T, q apptest.PrometheusQuerier, opts apptest.Quer
|
||||||
// query is actually an instant query executed 1 + (start-end)/step times on the
|
// query is actually an instant query executed 1 + (start-end)/step times on the
|
||||||
// time range from start to end. See:
|
// time range from start to end. See:
|
||||||
// https://docs.victoriametrics.com/keyconcepts/#range-query
|
// https://docs.victoriametrics.com/keyconcepts/#range-query
|
||||||
func testRangeQueryIsEquivalentToManyInstantQueries(t *testing.T, q apptest.PrometheusQuerier, opts apptest.QueryOpts) {
|
func testRangeQueryIsEquivalentToManyInstantQueries(t *testing.T, q apptest.PrometheusQuerier) {
|
||||||
f := func(timestamp string, want *apptest.Sample) {
|
f := func(timestamp string, want *apptest.Sample) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
gotInstant := q.PrometheusAPIV1Query(t, "foo_bar", timestamp, "1m", opts)
|
gotInstant := q.PrometheusAPIV1Query(t, "foo_bar", apptest.QueryOpts{Time: timestamp, Step: "1m"})
|
||||||
if want == nil {
|
if want == nil {
|
||||||
if got, want := len(gotInstant.Data.Result), 0; got != want {
|
if got, want := len(gotInstant.Data.Result), 0; got != want {
|
||||||
t.Errorf("unexpected instant result size: got %d, want %d", got, want)
|
t.Errorf("unexpected instant result size: got %d, want %d", got, want)
|
||||||
|
@ -179,7 +178,11 @@ func testRangeQueryIsEquivalentToManyInstantQueries(t *testing.T, q apptest.Prom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rangeRes := q.PrometheusAPIV1QueryRange(t, "foo_bar", "2022-05-10T07:59:00.000Z", "2022-05-10T08:17:00.000Z", "1m", opts)
|
rangeRes := q.PrometheusAPIV1QueryRange(t, "foo_bar", apptest.QueryOpts{
|
||||||
|
Start: "2022-05-10T07:59:00.000Z",
|
||||||
|
End: "2022-05-10T08:17:00.000Z",
|
||||||
|
Step: "1m",
|
||||||
|
})
|
||||||
rangeSamples := rangeRes.Data.Result[0].Samples
|
rangeSamples := rangeRes.Data.Result[0].Samples
|
||||||
|
|
||||||
f("2022-05-10T07:59:00.000Z", nil)
|
f("2022-05-10T07:59:00.000Z", nil)
|
||||||
|
|
|
@ -62,9 +62,8 @@ func TestClusterInstantQueryDoesNotReturnStaleNaNs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInstantQueryDoesNotReturnStaleNaNs(t *testing.T, sut apptest.PrometheusWriteQuerier) {
|
func testInstantQueryDoesNotReturnStaleNaNs(t *testing.T, sut apptest.PrometheusWriteQuerier) {
|
||||||
opts := apptest.QueryOpts{Timeout: "5s", Tenant: "0"}
|
|
||||||
|
|
||||||
sut.PrometheusAPIV1Write(t, staleNaNsData, opts)
|
sut.PrometheusAPIV1Write(t, staleNaNsData, apptest.QueryOpts{})
|
||||||
sut.ForceFlush(t)
|
sut.ForceFlush(t)
|
||||||
|
|
||||||
var got, want *apptest.PrometheusAPIV1QueryResponse
|
var got, want *apptest.PrometheusAPIV1QueryResponse
|
||||||
|
@ -75,7 +74,10 @@ func testInstantQueryDoesNotReturnStaleNaNs(t *testing.T, sut apptest.Prometheus
|
||||||
|
|
||||||
// Verify that instant query returns the first point.
|
// Verify that instant query returns the first point.
|
||||||
|
|
||||||
got = sut.PrometheusAPIV1Query(t, "metric", "2024-01-01T00:01:00.000Z", "5m", opts)
|
got = sut.PrometheusAPIV1Query(t, "metric", apptest.QueryOpts{
|
||||||
|
Step: "5m",
|
||||||
|
Time: "2024-01-01T00:01:00.000Z",
|
||||||
|
})
|
||||||
want = apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": [{"metric": {"__name__": "metric"}}]}}`)
|
want = apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": [{"metric": {"__name__": "metric"}}]}}`)
|
||||||
want.Data.Result[0].Sample = apptest.NewSample(t, "2024-01-01T00:01:00Z", 1)
|
want.Data.Result[0].Sample = apptest.NewSample(t, "2024-01-01T00:01:00Z", 1)
|
||||||
if diff := cmp.Diff(want, got, cmpOptions...); diff != "" {
|
if diff := cmp.Diff(want, got, cmpOptions...); diff != "" {
|
||||||
|
@ -84,7 +86,10 @@ func testInstantQueryDoesNotReturnStaleNaNs(t *testing.T, sut apptest.Prometheus
|
||||||
|
|
||||||
// Verify that instant query does not return stale NaN.
|
// Verify that instant query does not return stale NaN.
|
||||||
|
|
||||||
got = sut.PrometheusAPIV1Query(t, "metric", "2024-01-01T00:02:00.000Z", "5m", opts)
|
got = sut.PrometheusAPIV1Query(t, "metric", apptest.QueryOpts{
|
||||||
|
Step: "5m",
|
||||||
|
Time: "2024-01-01T00:02:00.000Z",
|
||||||
|
})
|
||||||
want = apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": []}}`)
|
want = apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": []}}`)
|
||||||
// Empty response, stale NaN is not included into response
|
// Empty response, stale NaN is not included into response
|
||||||
if diff := cmp.Diff(want, got, cmpOptions...); diff != "" {
|
if diff := cmp.Diff(want, got, cmpOptions...); diff != "" {
|
||||||
|
@ -95,7 +100,10 @@ func testInstantQueryDoesNotReturnStaleNaNs(t *testing.T, sut apptest.Prometheus
|
||||||
// while it must not.
|
// while it must not.
|
||||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5806
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5806
|
||||||
|
|
||||||
got = sut.PrometheusAPIV1Query(t, "metric[2m]", "2024-01-01T00:02:00.000Z", "5m", opts)
|
got = sut.PrometheusAPIV1Query(t, "metric[2m]", apptest.QueryOpts{
|
||||||
|
Step: "5m",
|
||||||
|
Time: "2024-01-01T00:02:00.000Z",
|
||||||
|
})
|
||||||
want = apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": [{"metric": {"__name__": "metric"}, "values": []}]}}`)
|
want = apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": [{"metric": {"__name__": "metric"}, "values": []}]}}`)
|
||||||
s := make([]*apptest.Sample, 2)
|
s := make([]*apptest.Sample, 2)
|
||||||
s[0] = apptest.NewSample(t, "2024-01-01T00:01:00Z", 1)
|
s[0] = apptest.NewSample(t, "2024-01-01T00:01:00Z", 1)
|
||||||
|
@ -107,7 +115,10 @@ func testInstantQueryDoesNotReturnStaleNaNs(t *testing.T, sut apptest.Prometheus
|
||||||
|
|
||||||
// Verify that exported data contains stale NaN.
|
// Verify that exported data contains stale NaN.
|
||||||
|
|
||||||
got = sut.PrometheusAPIV1Export(t, `{__name__="metric"}`, "2024-01-01T00:01:00.000Z", "2024-01-01T00:02:00.000Z", opts)
|
got = sut.PrometheusAPIV1Export(t, `{__name__="metric"}`, apptest.QueryOpts{
|
||||||
|
Start: "2024-01-01T00:01:00.000Z",
|
||||||
|
End: "2024-01-01T00:02:00.000Z",
|
||||||
|
})
|
||||||
want = apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": [{"metric": {"__name__": "metric"}, "values": []}]}}`)
|
want = apptest.NewPrometheusAPIV1QueryResponse(t, `{"data": {"result": [{"metric": {"__name__": "metric"}, "values": []}]}}`)
|
||||||
s = make([]*apptest.Sample, 2)
|
s = make([]*apptest.Sample, 2)
|
||||||
s[0] = apptest.NewSample(t, "2024-01-01T00:01:00Z", 1)
|
s[0] = apptest.NewSample(t, "2024-01-01T00:01:00Z", 1)
|
||||||
|
|
174
apptest/tests/multitenant_test.go
Normal file
174
apptest/tests/multitenant_test.go
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/apptest"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClusterMultiTenantSelect(t *testing.T) {
|
||||||
|
os.RemoveAll(t.Name())
|
||||||
|
|
||||||
|
cmpOpt := cmpopts.IgnoreFields(apptest.PrometheusAPIV1QueryResponse{}, "Status", "Data.ResultType")
|
||||||
|
cmpSROpt := cmpopts.IgnoreFields(apptest.PrometheusAPIV1SeriesResponse{}, "Status", "IsPartial")
|
||||||
|
|
||||||
|
tc := apptest.NewTestCase(t)
|
||||||
|
defer tc.Stop()
|
||||||
|
vmstorage := tc.MustStartVmstorage("vmstorage", []string{
|
||||||
|
"-storageDataPath=" + tc.Dir() + "/vmstorage",
|
||||||
|
"-retentionPeriod=100y",
|
||||||
|
})
|
||||||
|
vminsert := tc.MustStartVminsert("vminsert", []string{
|
||||||
|
"-storageNode=" + vmstorage.VminsertAddr(),
|
||||||
|
})
|
||||||
|
vmselect := tc.MustStartVmselect("vmselect", []string{
|
||||||
|
"-storageNode=" + vmstorage.VmselectAddr(),
|
||||||
|
"-search.tenantCacheExpireDuration=0",
|
||||||
|
})
|
||||||
|
|
||||||
|
var commonSamples = []string{
|
||||||
|
`foo_bar 1.00 1652169600000`, // 2022-05-10T08:00:00Z
|
||||||
|
`foo_bar 2.00 1652169660000`, // 2022-05-10T08:01:00Z
|
||||||
|
`foo_bar 3.00 1652169720000`, // 2022-05-10T08:02:00Z
|
||||||
|
}
|
||||||
|
|
||||||
|
// test for empty tenants request
|
||||||
|
got := vmselect.PrometheusAPIV1Query(t, "foo_bar", apptest.QueryOpts{
|
||||||
|
Tenant: "multitenant",
|
||||||
|
Step: "5m",
|
||||||
|
Time: "2022-05-10T08:03:00.000Z",
|
||||||
|
})
|
||||||
|
want := apptest.NewPrometheusAPIV1QueryResponse(t, `{"data":{"result":[]}}`)
|
||||||
|
if diff := cmp.Diff(want, got, cmpOpt); diff != "" {
|
||||||
|
t.Errorf("unexpected response (-want, +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ingest per tenant data and verify it with search
|
||||||
|
tenantIDs := []string{"1:1", "1:15"}
|
||||||
|
instantCT := "2022-05-10T08:05:00.000Z"
|
||||||
|
for _, tenantID := range tenantIDs {
|
||||||
|
vminsert.PrometheusAPIV1ImportPrometheus(t, commonSamples, apptest.QueryOpts{Tenant: tenantID})
|
||||||
|
vmstorage.ForceFlush(t)
|
||||||
|
got := vmselect.PrometheusAPIV1Query(t, "foo_bar", apptest.QueryOpts{
|
||||||
|
Tenant: tenantID, Time: instantCT,
|
||||||
|
})
|
||||||
|
want := apptest.NewPrometheusAPIV1QueryResponse(t, `{"data":{"result":[{"metric":{"__name__":"foo_bar"},"value":[1652169900,"3"]}]}}`)
|
||||||
|
if diff := cmp.Diff(want, got, cmpOpt); diff != "" {
|
||||||
|
t.Errorf("unexpected response (-want, +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// verify all tenants searchable with multitenant APIs
|
||||||
|
|
||||||
|
// /api/v1/query
|
||||||
|
want = apptest.NewPrometheusAPIV1QueryResponse(t,
|
||||||
|
`{"data":
|
||||||
|
{"result":[
|
||||||
|
{"metric":{"__name__":"foo_bar","vm_account_id":"1","vm_project_id": "1"},"value":[1652169900,"3"]},
|
||||||
|
{"metric":{"__name__":"foo_bar","vm_account_id":"1","vm_project_id":"15"},"value":[1652169900,"3"]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
)
|
||||||
|
got = vmselect.PrometheusAPIV1Query(t, "foo_bar", apptest.QueryOpts{
|
||||||
|
Tenant: "multitenant",
|
||||||
|
Time: instantCT,
|
||||||
|
})
|
||||||
|
if diff := cmp.Diff(want, got, cmpOpt); diff != "" {
|
||||||
|
t.Errorf("unexpected response (-want, +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// /api/v1/query_range aggregated by tenant labels
|
||||||
|
query := "sum(foo_bar) by(vm_account_id,vm_project_id)"
|
||||||
|
got = vmselect.PrometheusAPIV1QueryRange(t, query, apptest.QueryOpts{
|
||||||
|
Tenant: "multitenant",
|
||||||
|
Start: "2022-05-10T07:59:00.000Z",
|
||||||
|
End: "2022-05-10T08:05:00.000Z",
|
||||||
|
Step: "1m",
|
||||||
|
})
|
||||||
|
|
||||||
|
want = apptest.NewPrometheusAPIV1QueryResponse(t,
|
||||||
|
`{"data":
|
||||||
|
{"result": [
|
||||||
|
{"metric": {"vm_account_id": "1","vm_project_id":"1"}, "values": [[1652169600,"1"],[1652169660,"2"],[1652169720,"3"],[1652169780,"3"]]},
|
||||||
|
{"metric": {"vm_account_id": "1","vm_project_id":"15"}, "values": [[1652169600,"1"],[1652169660,"2"],[1652169720,"3"],[1652169780,"3"]]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
if diff := cmp.Diff(want, got, cmpOpt); diff != "" {
|
||||||
|
t.Errorf("unexpected response (-want, +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify /api/v1/series response
|
||||||
|
|
||||||
|
wantSR := apptest.NewPrometheusAPIV1SeriesResponse(t,
|
||||||
|
`{"data": [
|
||||||
|
{"__name__":"foo_bar", "vm_account_id":"1", "vm_project_id":"1"},
|
||||||
|
{"__name__":"foo_bar", "vm_account_id":"1", "vm_project_id":"15"}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
wantSR.Sort()
|
||||||
|
|
||||||
|
gotSR := vmselect.PrometheusAPIV1Series(t, "foo_bar", apptest.QueryOpts{
|
||||||
|
Tenant: "multitenant",
|
||||||
|
Start: "2022-05-10T08:03:00.000Z",
|
||||||
|
})
|
||||||
|
gotSR.Sort()
|
||||||
|
if diff := cmp.Diff(wantSR, gotSR, cmpSROpt); diff != "" {
|
||||||
|
t.Errorf("unexpected response (-want, +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test multitenant ingest path, tenants must be populated from labels
|
||||||
|
//
|
||||||
|
var tenantLabelsSamples = []string{
|
||||||
|
`foo_bar{vm_account_id="5"} 1.00 1652169600000`, // 2022-05-10T08:00:00Z'
|
||||||
|
`foo_bar{vm_project_id="10"} 2.00 1652169660000`, // 2022-05-10T08:01:00Z
|
||||||
|
`foo_bar{vm_account_id="5",vm_project_id="15"} 3.00 1652169720000`, // 2022-05-10T08:02:00Z
|
||||||
|
}
|
||||||
|
|
||||||
|
vminsert.PrometheusAPIV1ImportPrometheus(t, tenantLabelsSamples, apptest.QueryOpts{Tenant: "multitenant"})
|
||||||
|
vmstorage.ForceFlush(t)
|
||||||
|
|
||||||
|
// /api/v1/query with query filters
|
||||||
|
want = apptest.NewPrometheusAPIV1QueryResponse(t,
|
||||||
|
`{"data":
|
||||||
|
{"result":[
|
||||||
|
{"metric":{"__name__":"foo_bar","vm_account_id":"5","vm_project_id": "0"},"value":[1652169900,"1"]},
|
||||||
|
{"metric":{"__name__":"foo_bar","vm_account_id":"5","vm_project_id":"15"},"value":[1652169900,"3"]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
)
|
||||||
|
got = vmselect.PrometheusAPIV1Query(t, `foo_bar{vm_account_id="5"}`, apptest.QueryOpts{
|
||||||
|
Time: instantCT,
|
||||||
|
Tenant: "multitenant",
|
||||||
|
})
|
||||||
|
if diff := cmp.Diff(want, got, cmpOpt); diff != "" {
|
||||||
|
t.Errorf("unexpected response (-want, +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
// /api/v1/series with extra_filters
|
||||||
|
|
||||||
|
wantSR = apptest.NewPrometheusAPIV1SeriesResponse(t,
|
||||||
|
`{"data": [
|
||||||
|
{"__name__":"foo_bar", "vm_account_id":"5", "vm_project_id":"15"},
|
||||||
|
{"__name__":"foo_bar", "vm_account_id":"1", "vm_project_id":"15"}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
wantSR.Sort()
|
||||||
|
gotSR = vmselect.PrometheusAPIV1Series(t, "foo_bar", apptest.QueryOpts{
|
||||||
|
Start: "2022-05-10T08:00:00.000Z",
|
||||||
|
End: "2022-05-10T08:30:00.000Z",
|
||||||
|
ExtraFilters: []string{`{vm_project_id="15"}`},
|
||||||
|
Tenant: "multitenant",
|
||||||
|
})
|
||||||
|
gotSR.Sort()
|
||||||
|
|
||||||
|
if diff := cmp.Diff(wantSR, gotSR, cmpSROpt); diff != "" {
|
||||||
|
t.Errorf("unexpected response (-want, +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -55,7 +55,7 @@ func StartVminsert(instance string, flags []string, cli *Client) (*Vminsert, err
|
||||||
func (app *Vminsert) PrometheusAPIV1Write(t *testing.T, records []pb.TimeSeries, opts QueryOpts) {
|
func (app *Vminsert) PrometheusAPIV1Write(t *testing.T, records []pb.TimeSeries, opts QueryOpts) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
url := fmt.Sprintf("http://%s/insert/%s/prometheus/api/v1/write", app.httpListenAddr, opts.Tenant)
|
url := fmt.Sprintf("http://%s/insert/%s/prometheus/api/v1/write", app.httpListenAddr, opts.getTenant())
|
||||||
wr := pb.WriteRequest{Timeseries: records}
|
wr := pb.WriteRequest{Timeseries: records}
|
||||||
data := snappy.Encode(nil, wr.MarshalProtobuf(nil))
|
data := snappy.Encode(nil, wr.MarshalProtobuf(nil))
|
||||||
app.sendBlocking(t, len(records), func() {
|
app.sendBlocking(t, len(records), func() {
|
||||||
|
@ -72,7 +72,12 @@ func (app *Vminsert) PrometheusAPIV1Write(t *testing.T, records []pb.TimeSeries,
|
||||||
func (app *Vminsert) PrometheusAPIV1ImportPrometheus(t *testing.T, records []string, opts QueryOpts) {
|
func (app *Vminsert) PrometheusAPIV1ImportPrometheus(t *testing.T, records []string, opts QueryOpts) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
url := fmt.Sprintf("http://%s/insert/%s/prometheus/api/v1/import/prometheus", app.httpListenAddr, opts.Tenant)
|
url := fmt.Sprintf("http://%s/insert/%s/prometheus/api/v1/import/prometheus", app.httpListenAddr, opts.getTenant())
|
||||||
|
uv := opts.asURLValues()
|
||||||
|
uvs := uv.Encode()
|
||||||
|
if len(uvs) > 0 {
|
||||||
|
url += "?" + uvs
|
||||||
|
}
|
||||||
data := []byte(strings.Join(records, "\n"))
|
data := []byte(strings.Join(records, "\n"))
|
||||||
app.sendBlocking(t, len(records), func() {
|
app.sendBlocking(t, len(records), func() {
|
||||||
app.cli.Post(t, url, "text/plain", data, http.StatusNoContent)
|
app.cli.Post(t, url, "text/plain", data, http.StatusNoContent)
|
||||||
|
|
|
@ -3,7 +3,6 @@ package apptest
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -60,15 +59,12 @@ func (app *Vmselect) ClusternativeListenAddr() string {
|
||||||
// /prometheus/api/v1/export vmselect endpoint.
|
// /prometheus/api/v1/export vmselect endpoint.
|
||||||
//
|
//
|
||||||
// See https://docs.victoriametrics.com/url-examples/#apiv1export
|
// See https://docs.victoriametrics.com/url-examples/#apiv1export
|
||||||
func (app *Vmselect) PrometheusAPIV1Export(t *testing.T, query, start, end string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
func (app *Vmselect) PrometheusAPIV1Export(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
exportURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/export", app.httpListenAddr, opts.Tenant)
|
exportURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/export", app.httpListenAddr, opts.getTenant())
|
||||||
values := url.Values{}
|
values := opts.asURLValues()
|
||||||
values.Add("match[]", query)
|
values.Add("match[]", query)
|
||||||
values.Add("start", start)
|
|
||||||
values.Add("end", end)
|
|
||||||
values.Add("timeout", opts.Timeout)
|
|
||||||
values.Add("format", "promapi")
|
values.Add("format", "promapi")
|
||||||
res := app.cli.PostForm(t, exportURL, values, http.StatusOK)
|
res := app.cli.PostForm(t, exportURL, values, http.StatusOK)
|
||||||
return NewPrometheusAPIV1QueryResponse(t, res)
|
return NewPrometheusAPIV1QueryResponse(t, res)
|
||||||
|
@ -79,15 +75,13 @@ func (app *Vmselect) PrometheusAPIV1Export(t *testing.T, query, start, end strin
|
||||||
// vmselect endpoint.
|
// vmselect endpoint.
|
||||||
//
|
//
|
||||||
// See https://docs.victoriametrics.com/url-examples/#apiv1query
|
// See https://docs.victoriametrics.com/url-examples/#apiv1query
|
||||||
func (app *Vmselect) PrometheusAPIV1Query(t *testing.T, query, time, step string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
func (app *Vmselect) PrometheusAPIV1Query(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
queryURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/query", app.httpListenAddr, opts.Tenant)
|
queryURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/query", app.httpListenAddr, opts.getTenant())
|
||||||
values := url.Values{}
|
values := opts.asURLValues()
|
||||||
values.Add("query", query)
|
values.Add("query", query)
|
||||||
values.Add("time", time)
|
|
||||||
values.Add("step", step)
|
|
||||||
values.Add("timeout", opts.Timeout)
|
|
||||||
res := app.cli.PostForm(t, queryURL, values, http.StatusOK)
|
res := app.cli.PostForm(t, queryURL, values, http.StatusOK)
|
||||||
return NewPrometheusAPIV1QueryResponse(t, res)
|
return NewPrometheusAPIV1QueryResponse(t, res)
|
||||||
}
|
}
|
||||||
|
@ -97,16 +91,13 @@ func (app *Vmselect) PrometheusAPIV1Query(t *testing.T, query, time, step string
|
||||||
// /prometheus/api/v1/query_range vmselect endpoint.
|
// /prometheus/api/v1/query_range vmselect endpoint.
|
||||||
//
|
//
|
||||||
// See https://docs.victoriametrics.com/url-examples/#apiv1query_range
|
// See https://docs.victoriametrics.com/url-examples/#apiv1query_range
|
||||||
func (app *Vmselect) PrometheusAPIV1QueryRange(t *testing.T, query, start, end, step string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
func (app *Vmselect) PrometheusAPIV1QueryRange(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
queryURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/query_range", app.httpListenAddr, opts.Tenant)
|
queryURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/query_range", app.httpListenAddr, opts.getTenant())
|
||||||
values := url.Values{}
|
values := opts.asURLValues()
|
||||||
values.Add("query", query)
|
values.Add("query", query)
|
||||||
values.Add("start", start)
|
|
||||||
values.Add("end", end)
|
|
||||||
values.Add("step", step)
|
|
||||||
values.Add("timeout", opts.Timeout)
|
|
||||||
res := app.cli.PostForm(t, queryURL, values, http.StatusOK)
|
res := app.cli.PostForm(t, queryURL, values, http.StatusOK)
|
||||||
return NewPrometheusAPIV1QueryResponse(t, res)
|
return NewPrometheusAPIV1QueryResponse(t, res)
|
||||||
}
|
}
|
||||||
|
@ -118,9 +109,10 @@ func (app *Vmselect) PrometheusAPIV1QueryRange(t *testing.T, query, start, end,
|
||||||
func (app *Vmselect) PrometheusAPIV1Series(t *testing.T, matchQuery string, opts QueryOpts) *PrometheusAPIV1SeriesResponse {
|
func (app *Vmselect) PrometheusAPIV1Series(t *testing.T, matchQuery string, opts QueryOpts) *PrometheusAPIV1SeriesResponse {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
seriesURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/series", app.httpListenAddr, opts.Tenant)
|
seriesURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/series", app.httpListenAddr, opts.getTenant())
|
||||||
values := url.Values{}
|
values := opts.asURLValues()
|
||||||
values.Add("match[]", matchQuery)
|
values.Add("match[]", matchQuery)
|
||||||
|
|
||||||
res := app.cli.PostForm(t, seriesURL, values, http.StatusOK)
|
res := app.cli.PostForm(t, seriesURL, values, http.StatusOK)
|
||||||
return NewPrometheusAPIV1SeriesResponse(t, res)
|
return NewPrometheusAPIV1SeriesResponse(t, res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package apptest
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -110,15 +109,12 @@ func (app *Vmsingle) PrometheusAPIV1ImportPrometheus(t *testing.T, records []str
|
||||||
// /prometheus/api/v1/export vmsingle endpoint.
|
// /prometheus/api/v1/export vmsingle endpoint.
|
||||||
//
|
//
|
||||||
// See https://docs.victoriametrics.com/url-examples/#apiv1export
|
// See https://docs.victoriametrics.com/url-examples/#apiv1export
|
||||||
func (app *Vmsingle) PrometheusAPIV1Export(t *testing.T, query, start, end string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
func (app *Vmsingle) PrometheusAPIV1Export(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
values := opts.asURLValues()
|
||||||
values := url.Values{}
|
|
||||||
values.Add("match[]", query)
|
values.Add("match[]", query)
|
||||||
values.Add("start", start)
|
|
||||||
values.Add("end", end)
|
|
||||||
values.Add("timeout", opts.Timeout)
|
|
||||||
values.Add("format", "promapi")
|
values.Add("format", "promapi")
|
||||||
|
|
||||||
res := app.cli.PostForm(t, app.prometheusAPIV1ExportURL, values, http.StatusOK)
|
res := app.cli.PostForm(t, app.prometheusAPIV1ExportURL, values, http.StatusOK)
|
||||||
return NewPrometheusAPIV1QueryResponse(t, res)
|
return NewPrometheusAPIV1QueryResponse(t, res)
|
||||||
}
|
}
|
||||||
|
@ -128,14 +124,11 @@ func (app *Vmsingle) PrometheusAPIV1Export(t *testing.T, query, start, end strin
|
||||||
// vmsingle endpoint.
|
// vmsingle endpoint.
|
||||||
//
|
//
|
||||||
// See https://docs.victoriametrics.com/url-examples/#apiv1query
|
// See https://docs.victoriametrics.com/url-examples/#apiv1query
|
||||||
func (app *Vmsingle) PrometheusAPIV1Query(t *testing.T, query, time, step string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
func (app *Vmsingle) PrometheusAPIV1Query(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
values := url.Values{}
|
values := opts.asURLValues()
|
||||||
values.Add("query", query)
|
values.Add("query", query)
|
||||||
values.Add("time", time)
|
|
||||||
values.Add("step", step)
|
|
||||||
values.Add("timeout", opts.Timeout)
|
|
||||||
res := app.cli.PostForm(t, app.prometheusAPIV1QueryURL, values, http.StatusOK)
|
res := app.cli.PostForm(t, app.prometheusAPIV1QueryURL, values, http.StatusOK)
|
||||||
return NewPrometheusAPIV1QueryResponse(t, res)
|
return NewPrometheusAPIV1QueryResponse(t, res)
|
||||||
}
|
}
|
||||||
|
@ -145,15 +138,12 @@ func (app *Vmsingle) PrometheusAPIV1Query(t *testing.T, query, time, step string
|
||||||
// /prometheus/api/v1/query_range vmsingle endpoint.
|
// /prometheus/api/v1/query_range vmsingle endpoint.
|
||||||
//
|
//
|
||||||
// See https://docs.victoriametrics.com/url-examples/#apiv1query_range
|
// See https://docs.victoriametrics.com/url-examples/#apiv1query_range
|
||||||
func (app *Vmsingle) PrometheusAPIV1QueryRange(t *testing.T, query, start, end, step string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
func (app *Vmsingle) PrometheusAPIV1QueryRange(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
values := url.Values{}
|
values := opts.asURLValues()
|
||||||
values.Add("query", query)
|
values.Add("query", query)
|
||||||
values.Add("start", start)
|
|
||||||
values.Add("end", end)
|
|
||||||
values.Add("step", step)
|
|
||||||
values.Add("timeout", opts.Timeout)
|
|
||||||
res := app.cli.PostForm(t, app.prometheusAPIV1QueryRangeURL, values, http.StatusOK)
|
res := app.cli.PostForm(t, app.prometheusAPIV1QueryRangeURL, values, http.StatusOK)
|
||||||
return NewPrometheusAPIV1QueryResponse(t, res)
|
return NewPrometheusAPIV1QueryResponse(t, res)
|
||||||
}
|
}
|
||||||
|
@ -162,11 +152,12 @@ func (app *Vmsingle) PrometheusAPIV1QueryRange(t *testing.T, query, start, end,
|
||||||
// and returns the list of time series that match the query.
|
// and returns the list of time series that match the query.
|
||||||
//
|
//
|
||||||
// See https://docs.victoriametrics.com/url-examples/#apiv1series
|
// See https://docs.victoriametrics.com/url-examples/#apiv1series
|
||||||
func (app *Vmsingle) PrometheusAPIV1Series(t *testing.T, matchQuery string, _ QueryOpts) *PrometheusAPIV1SeriesResponse {
|
func (app *Vmsingle) PrometheusAPIV1Series(t *testing.T, matchQuery string, opts QueryOpts) *PrometheusAPIV1SeriesResponse {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
values := url.Values{}
|
values := opts.asURLValues()
|
||||||
values.Add("match[]", matchQuery)
|
values.Add("match[]", matchQuery)
|
||||||
|
|
||||||
res := app.cli.PostForm(t, app.prometheusAPIV1SeriesURL, values, http.StatusOK)
|
res := app.cli.PostForm(t, app.prometheusAPIV1SeriesURL, values, http.StatusOK)
|
||||||
return NewPrometheusAPIV1SeriesResponse(t, res)
|
return NewPrometheusAPIV1SeriesResponse(t, res)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue