diff --git a/README.md b/README.md index c55aad5f0..0468a5a43 100644 --- a/README.md +++ b/README.md @@ -605,6 +605,10 @@ VictoriaMetrics supports the following Graphite APIs, which are needed for [Grap All the Graphite handlers can be pre-pended with `/graphite` prefix. For example, both `/graphite/metrics/find` and `/metrics/find` should work. +VictoriaMetrics accepts optional `extra_label==` query arg for all the Graphite APIs. This arg can be used for limiting the scope of time series +visible to the given tenant. It is expected that the `extra_label` query arg is automatically set by auth proxy sitting in front of VictoriaMetrics. +[Contact us](mailto:sales@victoriametrics.com) if you need assistance with such a proxy. + VictoriaMetrics supports `__graphite__` pseudo-label for filtering time series with Graphite-compatible filters in [MetricsQL](https://victoriametrics.github.io/MetricsQL.html). For example, `{__graphite__="foo.*.bar"}` is equivalent to `{__name__=~"foo[.][^.]*[.]bar"}`, but it works faster and it is easier to use when migrating from Graphite to VictoriaMetrics. diff --git a/app/vmselect/graphite/tags_api.go b/app/vmselect/graphite/tags_api.go index 5d80f1213..3344cb6ae 100644 --- a/app/vmselect/graphite/tags_api.go +++ b/app/vmselect/graphite/tags_api.go @@ -32,13 +32,17 @@ func TagsDelSeriesHandler(startTime time.Time, w http.ResponseWriter, r *http.Re var row graphiteparser.Row var tagsPool []graphiteparser.Tag ct := startTime.UnixNano() / 1e6 + etfs, err := searchutils.GetEnforcedTagFiltersFromRequest(r) + if err != nil { + return fmt.Errorf("cannot setup tag filters: %w", err) + } for _, path := range paths { var err error tagsPool, err = row.UnmarshalMetricAndTags(path, tagsPool[:0]) if err != nil { return fmt.Errorf("cannot parse path=%q: %w", path, err) } - tfs := make([]storage.TagFilter, 0, 1+len(row.Tags)) + tfs := make([]storage.TagFilter, 0, 1+len(row.Tags)+len(etfs)) tfs = append(tfs, storage.TagFilter{ Key: nil, Value: []byte(row.Metric), @@ -49,6 +53,7 @@ func TagsDelSeriesHandler(startTime time.Time, w http.ResponseWriter, r *http.Re Value: []byte(tag.Value), }) } + tfs = append(tfs, etfs...) sq := storage.NewSearchQuery(0, ct, [][]storage.TagFilter{tfs}) n, err := netstorage.DeleteSeries(sq, deadline) if err != nil { @@ -176,7 +181,11 @@ func TagsAutoCompleteValuesHandler(startTime time.Time, w http.ResponseWriter, r valuePrefix := r.FormValue("valuePrefix") exprs := r.Form["expr"] var tagValues []string - if len(exprs) == 0 { + etfs, err := searchutils.GetEnforcedTagFiltersFromRequest(r) + if err != nil { + return fmt.Errorf("cannot setup tag filters: %w", err) + } + if len(exprs) == 0 && len(etfs) == 0 { // Fast path: there are no `expr` filters, so use netstorage.GetGraphiteTagValues. // Escape special chars in tagPrefix as Graphite does. // See https://github.com/graphite-project/graphite-web/blob/3ad279df5cb90b211953e39161df416e54a84948/webapp/graphite/tags/base.py#L228 @@ -187,7 +196,7 @@ func TagsAutoCompleteValuesHandler(startTime time.Time, w http.ResponseWriter, r } } else { // Slow path: use netstorage.SearchMetricNames for applying `expr` filters. - sq, err := getSearchQueryForExprs(startTime, exprs) + sq, err := getSearchQueryForExprs(startTime, etfs, exprs) if err != nil { return err } @@ -257,7 +266,11 @@ func TagsAutoCompleteTagsHandler(startTime time.Time, w http.ResponseWriter, r * tagPrefix := r.FormValue("tagPrefix") exprs := r.Form["expr"] var labels []string - if len(exprs) == 0 { + etfs, err := searchutils.GetEnforcedTagFiltersFromRequest(r) + if err != nil { + return fmt.Errorf("cannot setup tag filters: %w", err) + } + if len(exprs) == 0 && len(etfs) == 0 { // Fast path: there are no `expr` filters, so use netstorage.GetGraphiteTags. // Escape special chars in tagPrefix as Graphite does. @@ -269,7 +282,7 @@ func TagsAutoCompleteTagsHandler(startTime time.Time, w http.ResponseWriter, r * } } else { // Slow path: use netstorage.SearchMetricNames for applying `expr` filters. - sq, err := getSearchQueryForExprs(startTime, exprs) + sq, err := getSearchQueryForExprs(startTime, etfs, exprs) if err != nil { return err } @@ -332,7 +345,11 @@ func TagsFindSeriesHandler(startTime time.Time, w http.ResponseWriter, r *http.R if len(exprs) == 0 { return fmt.Errorf("expecting at least one `expr` query arg") } - sq, err := getSearchQueryForExprs(startTime, exprs) + etfs, err := searchutils.GetEnforcedTagFiltersFromRequest(r) + if err != nil { + return fmt.Errorf("cannot setup tag filters: %w", err) + } + sq, err := getSearchQueryForExprs(startTime, etfs, exprs) if err != nil { return err } @@ -457,12 +474,13 @@ func getInt(r *http.Request, argName string) (int, error) { return n, nil } -func getSearchQueryForExprs(startTime time.Time, exprs []string) (*storage.SearchQuery, error) { +func getSearchQueryForExprs(startTime time.Time, etfs []storage.TagFilter, exprs []string) (*storage.SearchQuery, error) { tfs, err := exprsToTagFilters(exprs) if err != nil { return nil, err } ct := startTime.UnixNano() / 1e6 + tfs = append(tfs, etfs...) sq := storage.NewSearchQuery(0, ct, [][]storage.TagFilter{tfs}) return sq, nil } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c646f3f60..69f1abe5c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,6 +8,7 @@ * `process_resident_memory_shared_bytes` - RSS share for memory shared with other processes (aka shared memory). This share can be freed by the OS at any time, so it must be ignored by OOM killer. * `process_resident_memory_peak_bytes` - peak RSS usage for the process. * `process_virtual_memory_peak_bytes` - peak virtual memory usage for the process. +* FEATURE: accept and enforce `extra_label==` query arg at [Graphite APIs](https://victoriametrics.github.io/#graphite-api-usage). * BUGFIX: prevent from infinite loop on `{__graphite__="..."}` filters when a metric name contains `*`, `{` or `[` chars. * BUGFIX: prevent from infinite loop in `/metrics/find` and `/metrics/expand` [Graphite Metrics API handlers](https://victoriametrics.github.io/#graphite-metrics-api-usage) when they match metric names or labels with `*`, `{` or `[` chars. diff --git a/docs/Single-server-VictoriaMetrics.md b/docs/Single-server-VictoriaMetrics.md index c55aad5f0..0468a5a43 100644 --- a/docs/Single-server-VictoriaMetrics.md +++ b/docs/Single-server-VictoriaMetrics.md @@ -605,6 +605,10 @@ VictoriaMetrics supports the following Graphite APIs, which are needed for [Grap All the Graphite handlers can be pre-pended with `/graphite` prefix. For example, both `/graphite/metrics/find` and `/metrics/find` should work. +VictoriaMetrics accepts optional `extra_label==` query arg for all the Graphite APIs. This arg can be used for limiting the scope of time series +visible to the given tenant. It is expected that the `extra_label` query arg is automatically set by auth proxy sitting in front of VictoriaMetrics. +[Contact us](mailto:sales@victoriametrics.com) if you need assistance with such a proxy. + VictoriaMetrics supports `__graphite__` pseudo-label for filtering time series with Graphite-compatible filters in [MetricsQL](https://victoriametrics.github.io/MetricsQL.html). For example, `{__graphite__="foo.*.bar"}` is equivalent to `{__name__=~"foo[.][^.]*[.]bar"}`, but it works faster and it is easier to use when migrating from Graphite to VictoriaMetrics.