Skip to content

Commit e2598e0

Browse files
app/vlselect: introduce -vmalert.proxyURL flag, which enables proxying requests to /select/vmalert path to VMAlert
1 parent e9d0d7b commit e2598e0

File tree

4 files changed

+122
-0
lines changed

4 files changed

+122
-0
lines changed

app/vlselect/main.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ package vlselect
33
import (
44
"context"
55
"embed"
6+
"encoding/json"
67
"flag"
78
"fmt"
89
"net/http"
10+
nethttputil "net/http/httputil"
11+
"net/url"
912
"strings"
1013
"time"
1114

15+
"github.com/VictoriaMetrics/VictoriaMetrics/lib/buildinfo"
1216
"github.com/VictoriaMetrics/VictoriaMetrics/lib/cgroup"
1317
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver"
1418
"github.com/VictoriaMetrics/VictoriaMetrics/lib/httputil"
@@ -29,6 +33,10 @@ var (
2933

3034
disableSelect = flag.Bool("select.disable", false, "Whether to disable /select/* HTTP endpoints")
3135
disableInternal = flag.Bool("internalselect.disable", false, "Whether to disable /internal/select/* HTTP endpoints")
36+
37+
vmalertProxyURL = flag.String("vmalert.proxyURL", "", "Optional URL for proxying requests to vmalert"+
38+
"See https://docs.victoriametrics.com/victorialogs#vmalert.")
39+
disableTenantResponseHeaders = flag.Bool("search.disableTenantResponseHeaders", false, "Disable tenant information on UI")
3240
)
3341

3442
func getDefaultMaxConcurrentRequests() int {
@@ -48,6 +56,8 @@ func getDefaultMaxConcurrentRequests() int {
4856
// Init initializes vlselect
4957
func Init() {
5058
concurrencyLimitCh = make(chan struct{}, *maxConcurrentRequests)
59+
initVMUIConfig()
60+
initVMAlertProxy()
5161
}
5262

5363
// Stop stops vlselect
@@ -111,6 +121,26 @@ func selectHandler(w http.ResponseWriter, r *http.Request, path string) bool {
111121
return true
112122
}
113123
if strings.HasPrefix(path, "/select/vmui/") {
124+
if path == "/select/vmui/config.json" {
125+
w.Header().Set("Content-Type", "application/json")
126+
r.URL.Path = strings.TrimPrefix(path, "/select")
127+
accountID := r.Header.Get("AccountID")
128+
projectID := r.Header.Get("ProjectID")
129+
if len(accountID) > 0 || len(projectID) > 0 {
130+
if *disableTenantResponseHeaders {
131+
w.Header().Set("VL-Disable-Tenant-Controls", "true")
132+
} else {
133+
if len(accountID) > 0 {
134+
w.Header().Set("AccountID", accountID)
135+
}
136+
if len(projectID) > 0 {
137+
w.Header().Set("ProjectID", projectID)
138+
}
139+
}
140+
}
141+
fmt.Fprint(w, vmuiConfig)
142+
return true
143+
}
114144
if strings.HasPrefix(path, "/select/vmui/static/") {
115145
// Allow clients caching static contents for long period of time, since it shouldn't change over time.
116146
// Path to static contents (such as js and css) must be changed whenever its contents is changed.
@@ -212,6 +242,17 @@ func decRequestConcurrency() {
212242
func processSelectRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, path string) bool {
213243
httpserver.EnableCORS(w, r)
214244
startTime := time.Now()
245+
if strings.HasPrefix(path, "/select/vmalert/") {
246+
vmalertRequests.Inc()
247+
if len(*vmalertProxyURL) == 0 {
248+
w.WriteHeader(http.StatusBadRequest)
249+
w.Header().Set("Content-Type", "application/json")
250+
fmt.Fprintf(w, "%s", `{"status":"error","msg":"for accessing vmalert flag '-vmalert.proxyURL' must be configured"}`)
251+
return true
252+
}
253+
proxyVMAlertRequests(w, r)
254+
return true
255+
}
215256
switch path {
216257
case "/select/logsql/facets":
217258
logsqlFacetsRequests.Inc()
@@ -286,6 +327,72 @@ func getMaxQueryDuration(r *http.Request) time.Duration {
286327
return d
287328
}
288329

330+
func proxyVMAlertRequests(w http.ResponseWriter, r *http.Request) {
331+
defer func() {
332+
err := recover()
333+
if err == nil || err == http.ErrAbortHandler {
334+
// Suppress http.ErrAbortHandler panic.
335+
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1353
336+
return
337+
}
338+
// Forward other panics to the caller.
339+
panic(err)
340+
}()
341+
r.URL.Path = strings.TrimPrefix(r.URL.Path, "/select")
342+
r.Host = vmalertProxyHost
343+
vmalertProxy.ServeHTTP(w, r)
344+
}
345+
346+
func initVMUIConfig() {
347+
var cfg struct {
348+
Version string `json:"version"`
349+
License struct {
350+
Type string `json:"type"`
351+
} `json:"license"`
352+
VMAlert struct {
353+
Enabled bool `json:"enabled"`
354+
} `json:"vmalert"`
355+
}
356+
cfg.Version = buildinfo.Version
357+
if cfg.Version == "" {
358+
// buildinfo.ShortVersion() may return empty result for builds without tags
359+
cfg.Version = buildinfo.Version
360+
}
361+
data, err := vmuiFiles.ReadFile("vmui/config.json")
362+
if err != nil {
363+
logger.Fatalf("cannot read vmui default config: %s", err)
364+
}
365+
err = json.Unmarshal(data, &cfg)
366+
if err != nil {
367+
logger.Fatalf("cannot parse vmui default config: %s", err)
368+
}
369+
cfg.VMAlert.Enabled = len(*vmalertProxyURL) != 0
370+
data, err = json.Marshal(&cfg)
371+
if err != nil {
372+
logger.Fatalf("cannot create vmui config: %s", err)
373+
}
374+
vmuiConfig = string(data)
375+
}
376+
377+
// initVMAlertProxy must be called after flag.Parse(), since it uses command-line flags.
378+
func initVMAlertProxy() {
379+
if len(*vmalertProxyURL) == 0 {
380+
return
381+
}
382+
proxyURL, err := url.Parse(*vmalertProxyURL)
383+
if err != nil {
384+
logger.Fatalf("cannot parse -vmalert.proxyURL=%q: %s", *vmalertProxyURL, err)
385+
}
386+
vmalertProxyHost = proxyURL.Host
387+
vmalertProxy = nethttputil.NewSingleHostReverseProxy(proxyURL)
388+
}
389+
390+
var (
391+
vmalertProxyHost string
392+
vmalertProxy *nethttputil.ReverseProxy
393+
vmuiConfig string
394+
)
395+
289396
var (
290397
logsqlFacetsRequests = metrics.NewCounter(`vl_http_requests_total{path="/select/logsql/facets"}`)
291398
logsqlFacetsDuration = metrics.NewSummary(`vl_http_request_duration_seconds{path="/select/logsql/facets"}`)
@@ -322,4 +429,6 @@ var (
322429

323430
// no need to track duration for tail requests, as they usually take long time
324431
logsqlTailRequests = metrics.NewCounter(`vl_http_requests_total{path="/select/logsql/tail"}`)
432+
433+
vmalertRequests = metrics.NewCounter(`vl_http_requests_total{path="/select/vmalert"}`)
325434
)

docs/victorialogs/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ according to [these docs](https://docs.victoriametrics.com/victorialogs/quicksta
1818

1919
## tip
2020

21+
* FEATURE: proxy VMAlert requests at `/select/vmalert` path, when `-vmalert.proxyURL` flag is set. See [#8272](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/8272).
22+
* FEATURE: add `-search.disableTenantResponseHeaders` flag to disable showing tenant information on VMUI for case, when AccountID and ProjectID headers are set by some proxy.
23+
2124
## [v1.33.0](https://github.com/VictoriaMetrics/VictoriaLogs/releases/tag/v1.33.0)
2225

2326
Released at 2025-09-10

docs/victorialogs/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,13 @@ The following steps must be performed for restoring the partition data from back
339339

340340
It is also possible to use **the disk snapshot** feature provided by the operating system or cloud provider in order to perform a backup.
341341

342+
## vmalert
343+
344+
VictoriaLogs is capable of proxying requests to [VMAlert](https://docs.victoriametrics.com/victorialogs/vmalert/)
345+
when `-vmalert.proxyURL` flag is set. Use this feature for accessing VMAlert API through VictoriaLogs Web interface.
346+
347+
For accessing VMAlert API through VictoriaLogs configure `-vmalert.proxyURL` flag. All VMAlert endpoints become available at `http://<victorialogs-addr>:9428/select/vmalert`.
348+
342349
## Multitenancy
343350

344351
VictoriaLogs supports multitenancy. A tenant is identified by `(AccountID, ProjectID)` pair, where `AccountID` and `ProjectID` are arbitrary 32-bit unsigned integers.

docs/victorialogs/vmalert.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ and [`/select/logsql/stats_query_range`](https://docs.victoriametrics.com/victor
1818
These endpoints return the log stats in a format compatible with [Prometheus querying API](https://prometheus.io/docs/prometheus/latest/querying/api/#instant-queries).
1919
It allows using VictoriaLogs as the datasource in vmalert, creating alerting and recording rules via [LogsQL](https://docs.victoriametrics.com/victorialogs/logsql/).
2020

21+
Configure `-vmalert.proxyURL` on [VictoriaLogs](https://docs.victoriametrics.com/victorialogs/#vmalert){{% available_from "#" %}} to proxy requests to `vmalert`.
22+
Proxying is needed to access `vmalert`'s UI through [vmui](https://docs.victoriametrics.com/#vmui).
23+
2124
_Note: This page provides only integration instructions for vmalert and VictoriaLogs. See the full textbook for vmalert [here](https://docs.victoriametrics.com/victoriametrics/vmalert/).
2225

2326
## Quick Start

0 commit comments

Comments
 (0)