Skip to content

Commit ee9d85b

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

File tree

4 files changed

+117
-0
lines changed

4 files changed

+117
-0
lines changed

app/vlselect/main.go

Lines changed: 105 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,22 @@ 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+
if *disableTenantResponseHeaders {
128+
w.Header().Set("VL-Disable-Tenant-Controls", "true")
129+
} else {
130+
if accountID := r.Header.Get("AccountID"); len(accountID) > 0 {
131+
w.Header().Set("AccountID", accountID)
132+
}
133+
if projectID := r.Header.Get("ProjectID"); len(projectID) > 0 {
134+
w.Header().Set("ProjectID", projectID)
135+
}
136+
}
137+
fmt.Fprint(w, vmuiConfig)
138+
return true
139+
}
114140
if strings.HasPrefix(path, "/select/vmui/static/") {
115141
// Allow clients caching static contents for long period of time, since it shouldn't change over time.
116142
// Path to static contents (such as js and css) must be changed whenever its contents is changed.
@@ -212,6 +238,17 @@ func decRequestConcurrency() {
212238
func processSelectRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, path string) bool {
213239
httpserver.EnableCORS(w, r)
214240
startTime := time.Now()
241+
if strings.HasPrefix(path, "/select/vmalert/") {
242+
vmalertRequests.Inc()
243+
if len(*vmalertProxyURL) == 0 {
244+
w.WriteHeader(http.StatusBadRequest)
245+
w.Header().Set("Content-Type", "application/json")
246+
fmt.Fprintf(w, "%s", `{"status":"error","msg":"for accessing vmalert flag '-vmalert.proxyURL' must be configured"}`)
247+
return true
248+
}
249+
proxyVMAlertRequests(w, r)
250+
return true
251+
}
215252
switch path {
216253
case "/select/logsql/facets":
217254
logsqlFacetsRequests.Inc()
@@ -286,6 +323,72 @@ func getMaxQueryDuration(r *http.Request) time.Duration {
286323
return d
287324
}
288325

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

323426
// no need to track duration for tail requests, as they usually take long time
324427
logsqlTailRequests = metrics.NewCounter(`vl_http_requests_total{path="/select/logsql/tail"}`)
428+
429+
vmalertRequests = metrics.NewCounter(`vl_http_requests_total{path="/select/vmalert"}`)
325430
)

docs/victorialogs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ 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+
2123
## [v1.33.0](https://github.com/VictoriaMetrics/VictoriaLogs/releases/tag/v1.33.0)
2224

2325
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)