Skip to content

Commit 2d9f998

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

File tree

4 files changed

+116
-0
lines changed

4 files changed

+116
-0
lines changed

app/vlselect/main.go

Lines changed: 104 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,9 @@ 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.")
3239
)
3340

3441
func getDefaultMaxConcurrentRequests() int {
@@ -48,6 +55,8 @@ func getDefaultMaxConcurrentRequests() int {
4855
// Init initializes vlselect
4956
func Init() {
5057
concurrencyLimitCh = make(chan struct{}, *maxConcurrentRequests)
58+
initVMUIConfig()
59+
initVMAlertProxy()
5160
}
5261

5362
// Stop stops vlselect
@@ -111,6 +120,22 @@ func selectHandler(w http.ResponseWriter, r *http.Request, path string) bool {
111120
return true
112121
}
113122
if strings.HasPrefix(path, "/select/vmui/") {
123+
if path == "/select/vmui/config.json" {
124+
w.Header().Set("Content-Type", "application/json")
125+
r.URL.Path = strings.TrimPrefix(path, "/select")
126+
if disableTenantControls := r.Header.Get("VL-Disable-Tenant-Controls"); disableTenantControls == "true" {
127+
w.Header().Set("VL-Disable-Tenant-Controls", "true")
128+
} else {
129+
if accountID := r.Header.Get("AccountID"); len(accountID) > 0 {
130+
w.Header().Set("AccountID", accountID)
131+
}
132+
if projectID := r.Header.Get("ProjectID"); len(projectID) > 0 {
133+
w.Header().Set("ProjectID", projectID)
134+
}
135+
}
136+
fmt.Fprint(w, vmuiConfig)
137+
return true
138+
}
114139
if strings.HasPrefix(path, "/select/vmui/static/") {
115140
// Allow clients caching static contents for long period of time, since it shouldn't change over time.
116141
// Path to static contents (such as js and css) must be changed whenever its contents is changed.
@@ -212,6 +237,17 @@ func decRequestConcurrency() {
212237
func processSelectRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, path string) bool {
213238
httpserver.EnableCORS(w, r)
214239
startTime := time.Now()
240+
if strings.HasPrefix(path, "/select/vmalert/") {
241+
vmalertRequests.Inc()
242+
if len(*vmalertProxyURL) == 0 {
243+
w.Header().Set("Content-Type", "application/json")
244+
w.WriteHeader(http.StatusBadRequest)
245+
fmt.Fprintf(w, "%s", `{"status":"error","msg":"for accessing vmalert flag '-vmalert.proxyURL' must be configured"}`)
246+
return true
247+
}
248+
proxyVMAlertRequests(w, r)
249+
return true
250+
}
215251
switch path {
216252
case "/select/logsql/facets":
217253
logsqlFacetsRequests.Inc()
@@ -286,6 +322,72 @@ func getMaxQueryDuration(r *http.Request) time.Duration {
286322
return d
287323
}
288324

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

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

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
* BUGFIX: [LogsQL](https://docs.victoriametrics.com/victorialogs/logsql/): allow using unquoted [pipe names](https://docs.victoriametrics.com/victorialogs/logsql/#pipes) inside [LogsQL filters](https://docs.victoriametrics.com/victorialogs/logsql/#filters). For example, `fields.foo:bar` is allowed now, while previously it should be written as `"fields.foo":bar`. See [#669](https://github.com/VictoriaMetrics/VictoriaLogs/issues/669).
2224
* BUGFIX: properly detele unneeded directories at [Ossfs2 filesystem](https://www.alibabacloud.com/help/en/oss/developer-reference/ossfs-2-0/). See [#649](https://github.com/VictoriaMetrics/VictoriaLogs/issues/649). Thanks to @xiaozongyang for [the initial pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/9709).
2325
* BUGFIX: all [Enterprise version of VictoriaLogs](https://docs.victoriametrics.com/victoriametrics/enterprise/): fix support for automatic issuing of TLS certificates for HTTPS server via Let's Encrypt service using TLS-ALPN-01 challenge. See [Automatic issuing of TLS certificates](https://docs.victoriametrics.com/victorialogs/#automatic-issuing-of-tls-certificates) for more info.

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