diff --git a/app/vmselect/prometheus/prometheus.go b/app/vmselect/prometheus/prometheus.go index b0a251d49..5b2fdc451 100644 --- a/app/vmselect/prometheus/prometheus.go +++ b/app/vmselect/prometheus/prometheus.go @@ -285,7 +285,7 @@ func ExportHandler(startTime time.Time, at *auth.Token, w http.ResponseWriter, r if start >= end { end = start + defaultStep } - etf, err := getEnforcedTagFiltersFromRequest(r) + etf, err := searchutils.GetEnforcedTagFiltersFromRequest(r) if err != nil { return err } @@ -520,7 +520,7 @@ func LabelValuesHandler(startTime time.Time, at *auth.Token, labelName string, w if err := r.ParseForm(); err != nil { return fmt.Errorf("cannot parse form values: %w", err) } - etf, err := getEnforcedTagFiltersFromRequest(r) + etf, err := searchutils.GetEnforcedTagFiltersFromRequest(r) if err != nil { return err } @@ -745,7 +745,7 @@ func LabelsHandler(startTime time.Time, at *auth.Token, w http.ResponseWriter, r if err := r.ParseForm(); err != nil { return fmt.Errorf("cannot parse form values: %w", err) } - etf, err := getEnforcedTagFiltersFromRequest(r) + etf, err := searchutils.GetEnforcedTagFiltersFromRequest(r) if err != nil { return err } @@ -1018,7 +1018,7 @@ func QueryHandler(startTime time.Time, at *auth.Token, w http.ResponseWriter, r if len(query) > maxQueryLen.N { return fmt.Errorf("too long query; got %d bytes; mustn't exceed `-search.maxQueryLen=%d` bytes", len(query), maxQueryLen.N) } - etf, err := getEnforcedTagFiltersFromRequest(r) + etf, err := searchutils.GetEnforcedTagFiltersFromRequest(r) if err != nil { return err } @@ -1155,7 +1155,7 @@ func QueryRangeHandler(startTime time.Time, at *auth.Token, w http.ResponseWrite if err != nil { return err } - etf, err := getEnforcedTagFiltersFromRequest(r) + etf, err := searchutils.GetEnforcedTagFiltersFromRequest(r) if err != nil { return err } @@ -1308,26 +1308,6 @@ func getMaxLookback(r *http.Request) (int64, error) { return searchutils.GetDuration(r, "max_lookback", d) } -func getEnforcedTagFiltersFromRequest(r *http.Request) ([]storage.TagFilter, error) { - // fast path. - extraLabels := r.Form["extra_label"] - if len(extraLabels) == 0 { - return nil, nil - } - tagFilters := make([]storage.TagFilter, 0, len(extraLabels)) - for _, match := range extraLabels { - tmp := strings.SplitN(match, "=", 2) - if len(tmp) != 2 { - return nil, fmt.Errorf("`extra_label` query arg must have the format `name=value`; got %q", match) - } - tagFilters = append(tagFilters, storage.TagFilter{ - Key: []byte(tmp[0]), - Value: []byte(tmp[1]), - }) - } - return tagFilters, nil -} - func addEnforcedFiltersToTagFilterss(dstTfss [][]storage.TagFilter, enforcedFilters []storage.TagFilter) [][]storage.TagFilter { if len(dstTfss) == 0 { return [][]storage.TagFilter{ @@ -1361,7 +1341,7 @@ func getTagFilterssFromRequest(r *http.Request) ([][]storage.TagFilter, error) { if err != nil { return nil, err } - etf, err := getEnforcedTagFiltersFromRequest(r) + etf, err := searchutils.GetEnforcedTagFiltersFromRequest(r) if err != nil { return nil, err } diff --git a/app/vmselect/prometheus/prometheus_test.go b/app/vmselect/prometheus/prometheus_test.go index d376a928b..037884999 100644 --- a/app/vmselect/prometheus/prometheus_test.go +++ b/app/vmselect/prometheus/prometheus_test.go @@ -2,7 +2,6 @@ package prometheus import ( "math" - "net/http" "reflect" "testing" @@ -232,40 +231,3 @@ func Test_addEnforcedFiltersToTagFilterss(t *testing.T) { {tfFromKV("l2", "v2"), tfFromKV("ext-l1", "v2")}, }) } - -func Test_getEnforcedTagFiltersFromRequest(t *testing.T) { - httpReqWithForm := func(tfs []string) *http.Request { - return &http.Request{ - Form: map[string][]string{ - "extra_label": tfs, - }, - } - } - f := func(t *testing.T, r *http.Request, want []storage.TagFilter, wantErr bool) { - t.Helper() - got, err := getEnforcedTagFiltersFromRequest(r) - if (err != nil) != wantErr { - t.Fatalf("unexpected error: %v", err) - } - if !reflect.DeepEqual(got, want) { - t.Fatalf("unxpected result for getEnforcedTagFiltersFromRequest, \ngot: %v,\n want: %v", want, got) - } - } - - f(t, httpReqWithForm([]string{"label=value"}), - []storage.TagFilter{ - tfFromKV("label", "value"), - }, - false) - - f(t, httpReqWithForm([]string{"job=vmagent", "dc=gce"}), - []storage.TagFilter{tfFromKV("job", "vmagent"), tfFromKV("dc", "gce")}, - false, - ) - f(t, httpReqWithForm([]string{"bad_filter"}), - nil, - true, - ) - f(t, &http.Request{}, - nil, false) -} diff --git a/app/vmselect/searchutils/searchutils.go b/app/vmselect/searchutils/searchutils.go index 0730fc89e..def7e81d4 100644 --- a/app/vmselect/searchutils/searchutils.go +++ b/app/vmselect/searchutils/searchutils.go @@ -9,6 +9,8 @@ import ( "strings" "time" + "github.com/VictoriaMetrics/VictoriaMetrics/lib/storage" + "github.com/VictoriaMetrics/VictoriaMetrics/lib/fasttime" "github.com/VictoriaMetrics/metricsql" ) @@ -207,3 +209,24 @@ func (d *Deadline) String() string { elapsed := time.Since(startTime) return fmt.Sprintf("%.3f seconds (elapsed %.3f seconds); the timeout can be adjusted with `%s` command-line flag", d.timeout.Seconds(), elapsed.Seconds(), d.flagHint) } + +// GetEnforcedTagFiltersFromRequest returns additional filters from request. +func GetEnforcedTagFiltersFromRequest(r *http.Request) ([]storage.TagFilter, error) { + // fast path. + extraLabels := r.Form["extra_label"] + if len(extraLabels) == 0 { + return nil, nil + } + tagFilters := make([]storage.TagFilter, 0, len(extraLabels)) + for _, match := range extraLabels { + tmp := strings.SplitN(match, "=", 2) + if len(tmp) != 2 { + return nil, fmt.Errorf("`extra_label` query arg must have the format `name=value`; got %q", match) + } + tagFilters = append(tagFilters, storage.TagFilter{ + Key: []byte(tmp[0]), + Value: []byte(tmp[1]), + }) + } + return tagFilters, nil +} diff --git a/app/vmselect/searchutils/searchutils_test.go b/app/vmselect/searchutils/searchutils_test.go index c64484159..c47f72ede 100644 --- a/app/vmselect/searchutils/searchutils_test.go +++ b/app/vmselect/searchutils/searchutils_test.go @@ -4,7 +4,10 @@ import ( "fmt" "net/http" "net/url" + "reflect" "testing" + + "github.com/VictoriaMetrics/VictoriaMetrics/lib/storage" ) func TestGetTimeSuccess(t *testing.T) { @@ -76,3 +79,48 @@ func TestGetTimeError(t *testing.T) { f("-292273086-05-16T16:47:07Z") f("292277025-08-18T07:12:54.999999998Z") } + +// helper for tests +func tfFromKV(k, v string) storage.TagFilter { + return storage.TagFilter{ + Key: []byte(k), + Value: []byte(v), + } +} + +func TestGetEnforcedTagFiltersFromRequest(t *testing.T) { + httpReqWithForm := func(tfs []string) *http.Request { + return &http.Request{ + Form: map[string][]string{ + "extra_label": tfs, + }, + } + } + f := func(t *testing.T, r *http.Request, want []storage.TagFilter, wantErr bool) { + t.Helper() + got, err := GetEnforcedTagFiltersFromRequest(r) + if (err != nil) != wantErr { + t.Fatalf("unexpected error: %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Fatalf("unxpected result for getEnforcedTagFiltersFromRequest, \ngot: %v,\n want: %v", want, got) + } + } + + f(t, httpReqWithForm([]string{"label=value"}), + []storage.TagFilter{ + tfFromKV("label", "value"), + }, + false) + + f(t, httpReqWithForm([]string{"job=vmagent", "dc=gce"}), + []storage.TagFilter{tfFromKV("job", "vmagent"), tfFromKV("dc", "gce")}, + false, + ) + f(t, httpReqWithForm([]string{"bad_filter"}), + nil, + true, + ) + f(t, &http.Request{}, + nil, false) +}