mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-27 02:46:47 +01: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
@ -3,6 +3,7 @@ package apptest
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -14,9 +15,9 @@ import (
|
||||
|
||||
// PrometheusQuerier contains methods available to Prometheus-like HTTP API for Querying
|
||||
type PrometheusQuerier interface {
|
||||
PrometheusAPIV1Export(t *testing.T, query, start, end string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
||||
PrometheusAPIV1Query(t *testing.T, query, time, step string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
||||
PrometheusAPIV1QueryRange(t *testing.T, query, start, end, step string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
||||
PrometheusAPIV1Export(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
||||
PrometheusAPIV1Query(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
||||
PrometheusAPIV1QueryRange(t *testing.T, query string, opts QueryOpts) *PrometheusAPIV1QueryResponse
|
||||
PrometheusAPIV1Series(t *testing.T, matchQuery string, opts QueryOpts) *PrometheusAPIV1SeriesResponse
|
||||
}
|
||||
|
||||
@ -42,8 +43,43 @@ type PrometheusWriteQuerier interface {
|
||||
|
||||
// QueryOpts contains various params used for querying or ingesting data
|
||||
type QueryOpts struct {
|
||||
Tenant string
|
||||
Timeout string
|
||||
Tenant 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
|
||||
@ -60,7 +96,7 @@ func NewPrometheusAPIV1QueryResponse(t *testing.T, s string) *PrometheusAPIV1Que
|
||||
|
||||
res := &PrometheusAPIV1QueryResponse{}
|
||||
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
|
||||
}
|
||||
@ -101,7 +137,7 @@ func NewSample(t *testing.T, timeStr string, value float64) *Sample {
|
||||
// UnmarshalJSON populates the sample fields from a JSON string.
|
||||
func (s *Sample) UnmarshalJSON(b []byte) error {
|
||||
var (
|
||||
ts int64
|
||||
ts float64
|
||||
v string
|
||||
)
|
||||
raw := []any{&ts, &v}
|
||||
@ -111,7 +147,7 @@ func (s *Sample) UnmarshalJSON(b []byte) error {
|
||||
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))
|
||||
}
|
||||
s.Timestamp = ts
|
||||
s.Timestamp = int64(ts)
|
||||
var err error
|
||||
s.Value, err = strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
@ -135,7 +171,7 @@ func NewPrometheusAPIV1SeriesResponse(t *testing.T, s string) *PrometheusAPIV1Se
|
||||
|
||||
res := &PrometheusAPIV1SeriesResponse{}
|
||||
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
|
||||
}
|
||||
|
@ -51,26 +51,25 @@ func TestClusterKeyConceptsQueryData(t *testing.T) {
|
||||
|
||||
// testClusterKeyConceptsQuery verifies cases from https://docs.victoriametrics.com/keyconcepts/#query-data
|
||||
func testKeyConceptsQueryData(t *testing.T, sut apptest.PrometheusWriteQuerier) {
|
||||
opts := apptest.QueryOpts{Timeout: "5s", Tenant: "0"}
|
||||
|
||||
// Insert example data from documentation.
|
||||
sut.PrometheusAPIV1ImportPrometheus(t, docData, opts)
|
||||
sut.PrometheusAPIV1ImportPrometheus(t, docData, apptest.QueryOpts{})
|
||||
sut.ForceFlush(t)
|
||||
|
||||
testInstantQuery(t, sut, opts)
|
||||
testRangeQuery(t, sut, opts)
|
||||
testRangeQueryIsEquivalentToManyInstantQueries(t, sut, opts)
|
||||
testInstantQuery(t, sut)
|
||||
testRangeQuery(t, sut)
|
||||
testRangeQueryIsEquivalentToManyInstantQueries(t, sut)
|
||||
}
|
||||
|
||||
// testInstantQuery verifies the statements made in the `Instant query` section
|
||||
// of the VictoriaMetrics documentation. See:
|
||||
// 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
|
||||
// step of 5m and timeout 5s. There is no sample at exactly this timestamp.
|
||||
// Therefore, VictoriaMetrics will search for the nearest sample within the
|
||||
// [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"]}]}}`)
|
||||
opt := cmpopts.IgnoreFields(apptest.PrometheusAPIV1QueryResponse{}, "Status", "Data.ResultType")
|
||||
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
|
||||
// [time-1m..time] interval. Since the nearest sample is 2m away and the
|
||||
// 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 {
|
||||
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
|
||||
// the VictoriaMetrics documentation. See:
|
||||
// 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) {
|
||||
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.Data.Result[0].Samples = wantSamples
|
||||
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
|
||||
// time range from start to end. See:
|
||||
// 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) {
|
||||
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 got, want := len(gotInstant.Data.Result), 0; 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
|
||||
|
||||
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) {
|
||||
opts := apptest.QueryOpts{Timeout: "5s", Tenant: "0"}
|
||||
|
||||
sut.PrometheusAPIV1Write(t, staleNaNsData, opts)
|
||||
sut.PrometheusAPIV1Write(t, staleNaNsData, apptest.QueryOpts{})
|
||||
sut.ForceFlush(t)
|
||||
|
||||
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.
|
||||
|
||||
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.Data.Result[0].Sample = apptest.NewSample(t, "2024-01-01T00:01:00Z", 1)
|
||||
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.
|
||||
|
||||
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": []}}`)
|
||||
// Empty response, stale NaN is not included into response
|
||||
if diff := cmp.Diff(want, got, cmpOptions...); diff != "" {
|
||||
@ -95,7 +100,10 @@ func testInstantQueryDoesNotReturnStaleNaNs(t *testing.T, sut apptest.Prometheus
|
||||
// while it must not.
|
||||
// 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": []}]}}`)
|
||||
s := make([]*apptest.Sample, 2)
|
||||
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.
|
||||
|
||||
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": []}]}}`)
|
||||
s = make([]*apptest.Sample, 2)
|
||||
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) {
|
||||
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}
|
||||
data := snappy.Encode(nil, wr.MarshalProtobuf(nil))
|
||||
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) {
|
||||
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"))
|
||||
app.sendBlocking(t, len(records), func() {
|
||||
app.cli.Post(t, url, "text/plain", data, http.StatusNoContent)
|
||||
|
@ -3,7 +3,6 @@ package apptest
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
@ -60,15 +59,12 @@ func (app *Vmselect) ClusternativeListenAddr() string {
|
||||
// /prometheus/api/v1/export vmselect endpoint.
|
||||
//
|
||||
// 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()
|
||||
|
||||
exportURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/export", app.httpListenAddr, opts.Tenant)
|
||||
values := url.Values{}
|
||||
exportURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/export", app.httpListenAddr, opts.getTenant())
|
||||
values := opts.asURLValues()
|
||||
values.Add("match[]", query)
|
||||
values.Add("start", start)
|
||||
values.Add("end", end)
|
||||
values.Add("timeout", opts.Timeout)
|
||||
values.Add("format", "promapi")
|
||||
res := app.cli.PostForm(t, exportURL, values, http.StatusOK)
|
||||
return NewPrometheusAPIV1QueryResponse(t, res)
|
||||
@ -79,15 +75,13 @@ func (app *Vmselect) PrometheusAPIV1Export(t *testing.T, query, start, end strin
|
||||
// vmselect endpoint.
|
||||
//
|
||||
// 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()
|
||||
|
||||
queryURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/query", app.httpListenAddr, opts.Tenant)
|
||||
values := url.Values{}
|
||||
queryURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/query", app.httpListenAddr, opts.getTenant())
|
||||
values := opts.asURLValues()
|
||||
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)
|
||||
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.
|
||||
//
|
||||
// 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()
|
||||
|
||||
queryURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/query_range", app.httpListenAddr, opts.Tenant)
|
||||
values := url.Values{}
|
||||
queryURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/query_range", app.httpListenAddr, opts.getTenant())
|
||||
values := opts.asURLValues()
|
||||
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)
|
||||
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 {
|
||||
t.Helper()
|
||||
|
||||
seriesURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/series", app.httpListenAddr, opts.Tenant)
|
||||
values := url.Values{}
|
||||
seriesURL := fmt.Sprintf("http://%s/select/%s/prometheus/api/v1/series", app.httpListenAddr, opts.getTenant())
|
||||
values := opts.asURLValues()
|
||||
values.Add("match[]", matchQuery)
|
||||
|
||||
res := app.cli.PostForm(t, seriesURL, values, http.StatusOK)
|
||||
return NewPrometheusAPIV1SeriesResponse(t, res)
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package apptest
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
@ -110,15 +109,12 @@ func (app *Vmsingle) PrometheusAPIV1ImportPrometheus(t *testing.T, records []str
|
||||
// /prometheus/api/v1/export vmsingle endpoint.
|
||||
//
|
||||
// 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()
|
||||
|
||||
values := url.Values{}
|
||||
values := opts.asURLValues()
|
||||
values.Add("match[]", query)
|
||||
values.Add("start", start)
|
||||
values.Add("end", end)
|
||||
values.Add("timeout", opts.Timeout)
|
||||
values.Add("format", "promapi")
|
||||
|
||||
res := app.cli.PostForm(t, app.prometheusAPIV1ExportURL, values, http.StatusOK)
|
||||
return NewPrometheusAPIV1QueryResponse(t, res)
|
||||
}
|
||||
@ -128,14 +124,11 @@ func (app *Vmsingle) PrometheusAPIV1Export(t *testing.T, query, start, end strin
|
||||
// vmsingle endpoint.
|
||||
//
|
||||
// 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()
|
||||
|
||||
values := url.Values{}
|
||||
values := opts.asURLValues()
|
||||
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)
|
||||
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.
|
||||
//
|
||||
// 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()
|
||||
|
||||
values := url.Values{}
|
||||
values := opts.asURLValues()
|
||||
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)
|
||||
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.
|
||||
//
|
||||
// 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()
|
||||
|
||||
values := url.Values{}
|
||||
values := opts.asURLValues()
|
||||
values.Add("match[]", matchQuery)
|
||||
|
||||
res := app.cli.PostForm(t, app.prometheusAPIV1SeriesURL, values, http.StatusOK)
|
||||
return NewPrometheusAPIV1SeriesResponse(t, res)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user