From bb99ddf957ecbe3ecde1800c29de751040fcf41c Mon Sep 17 00:00:00 2001 From: Nikolay Date: Tue, 26 Nov 2024 19:03:56 +0100 Subject: [PATCH] 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 --- apptest/model.go | 54 +++++++-- apptest/tests/key_concepts_test.go | 29 ++--- apptest/tests/metricsql_test.go | 23 +++- apptest/tests/multitenant_test.go | 174 +++++++++++++++++++++++++++++ apptest/vminsert.go | 9 +- apptest/vmselect.go | 36 +++--- apptest/vmsingle.go | 31 ++--- 7 files changed, 284 insertions(+), 72 deletions(-) create mode 100644 apptest/tests/multitenant_test.go diff --git a/apptest/model.go b/apptest/model.go index 2477a57e3..cdd9b707c 100644 --- a/apptest/model.go +++ b/apptest/model.go @@ -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 } diff --git a/apptest/tests/key_concepts_test.go b/apptest/tests/key_concepts_test.go index 8eeec4ef1..cb3757746 100644 --- a/apptest/tests/key_concepts_test.go +++ b/apptest/tests/key_concepts_test.go @@ -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) diff --git a/apptest/tests/metricsql_test.go b/apptest/tests/metricsql_test.go index 935ff5a2a..54a1dc92f 100644 --- a/apptest/tests/metricsql_test.go +++ b/apptest/tests/metricsql_test.go @@ -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) diff --git a/apptest/tests/multitenant_test.go b/apptest/tests/multitenant_test.go new file mode 100644 index 000000000..2087e4f2e --- /dev/null +++ b/apptest/tests/multitenant_test.go @@ -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) + } + +} diff --git a/apptest/vminsert.go b/apptest/vminsert.go index 82f291345..7cf6d0e99 100644 --- a/apptest/vminsert.go +++ b/apptest/vminsert.go @@ -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) diff --git a/apptest/vmselect.go b/apptest/vmselect.go index c606b2c0a..6e2bc6df5 100644 --- a/apptest/vmselect.go +++ b/apptest/vmselect.go @@ -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) } diff --git a/apptest/vmsingle.go b/apptest/vmsingle.go index c1aa77b40..55aa5dfd3 100644 --- a/apptest/vmsingle.go +++ b/apptest/vmsingle.go @@ -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) }