From 4fafdda13e524530f090217ddea55333c4522648 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Wed, 1 Nov 2023 19:53:50 +0800 Subject: [PATCH] vmalert: support specifying full http url in notifier static_configs target (#5261) * vmalert: support specifying full http or https urls in notifier static_configs target address * show right label results in ui --- app/vmalert/README.md | 5 +- app/vmalert/notifier/config.go | 51 ++++++++++++------- app/vmalert/notifier/config_watcher_test.go | 44 ++++++++++++++++ .../notifier/testdata/static.good.yaml | 1 + docs/CHANGELOG.md | 1 + docs/vmalert.md | 5 +- 6 files changed, 86 insertions(+), 21 deletions(-) diff --git a/app/vmalert/README.md b/app/vmalert/README.md index d0c6a0a4f1..4e76a6569f 100644 --- a/app/vmalert/README.md +++ b/app/vmalert/README.md @@ -1399,8 +1399,11 @@ For example: ```yaml static_configs: - targets: + # support using full url + - 'http://alertmanager:9093/test/api/v2/alerts' + - 'https://alertmanager:9093/api/v2/alerts' + # the following target with only host:port will be used as ://localhost:9093//api/v2/alerts - localhost:9093 - - localhost:9095 consul_sd_configs: - server: localhost:8500 diff --git a/app/vmalert/notifier/config.go b/app/vmalert/notifier/config.go index d6419b900e..44b22fb79d 100644 --- a/app/vmalert/notifier/config.go +++ b/app/vmalert/notifier/config.go @@ -3,7 +3,6 @@ package notifier import ( "crypto/md5" "fmt" - "gopkg.in/yaml.v2" "net/url" "os" "path" @@ -11,6 +10,8 @@ import ( "strings" "time" + "gopkg.in/yaml.v2" + "github.com/VictoriaMetrics/VictoriaMetrics/lib/promauth" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promrelabel" "github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discovery/consul" @@ -142,26 +143,23 @@ func parseLabels(target string, metaLabels *promutils.Labels, cfg *Config) (stri if labels.Len() == 0 { return "", nil, nil } - schemeRelabeled := labels.Get("__scheme__") - if len(schemeRelabeled) == 0 { - schemeRelabeled = "http" + scheme := labels.Get("__scheme__") + if len(scheme) == 0 { + scheme = "http" } - addressRelabeled := labels.Get("__address__") - if len(addressRelabeled) == 0 { + alertsPath := labels.Get("__alerts_path__") + if !strings.HasPrefix(alertsPath, "/") { + alertsPath = "/" + alertsPath + } + address := labels.Get("__address__") + if len(address) == 0 { return "", nil, nil } - if strings.Contains(addressRelabeled, "/") { - return "", nil, nil - } - addressRelabeled = addMissingPort(schemeRelabeled, addressRelabeled) - alertsPathRelabeled := labels.Get("__alerts_path__") - if !strings.HasPrefix(alertsPathRelabeled, "/") { - alertsPathRelabeled = "/" + alertsPathRelabeled - } - u := fmt.Sprintf("%s://%s%s", schemeRelabeled, addressRelabeled, alertsPathRelabeled) + address = addMissingPort(scheme, address) + u := fmt.Sprintf("%s://%s%s", scheme, address, alertsPath) if _, err := url.Parse(u); err != nil { return "", nil, fmt.Errorf("invalid url %q for scheme=%q (%q), target=%q, metrics_path=%q (%q): %w", - u, cfg.Scheme, schemeRelabeled, target, addressRelabeled, alertsPathRelabeled, err) + u, cfg.Scheme, scheme, target, address, alertsPath, err) } return u, labels, nil } @@ -181,9 +179,24 @@ func addMissingPort(scheme, target string) string { func mergeLabels(target string, metaLabels *promutils.Labels, cfg *Config) *promutils.Labels { // See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#relabel_config m := promutils.NewLabels(3 + metaLabels.Len()) - m.Add("__address__", target) - m.Add("__scheme__", cfg.Scheme) - m.Add("__alerts_path__", path.Join("/", cfg.PathPrefix, alertManagerPath)) + address := target + scheme := cfg.Scheme + alertsPath := path.Join("/", cfg.PathPrefix, alertManagerPath) + // try to extract optional scheme and alertsPath from __address__. + if strings.HasPrefix(address, "http://") { + scheme = "http" + address = address[len("http://"):] + } else if strings.HasPrefix(address, "https://") { + scheme = "https" + address = address[len("https://"):] + } + if n := strings.IndexByte(address, '/'); n >= 0 { + alertsPath = address[n:] + address = address[:n] + } + m.Add("__address__", address) + m.Add("__scheme__", scheme) + m.Add("__alerts_path__", alertsPath) m.AddFrom(metaLabels) return m } diff --git a/app/vmalert/notifier/config_watcher_test.go b/app/vmalert/notifier/config_watcher_test.go index b51cd3bfa2..2f00d72897 100644 --- a/app/vmalert/notifier/config_watcher_test.go +++ b/app/vmalert/notifier/config_watcher_test.go @@ -318,3 +318,47 @@ func TestMergeHTTPClientConfigs(t *testing.T) { t.Fatalf("expected BasicAuth tp be present") } } + +func TestParseLabels(t *testing.T) { + testCases := []struct { + name string + target string + cfg *Config + expectedAddress string + expectedErr bool + }{ + { + "invalid address", + "invalid:*//url", + &Config{}, + "", + true, + }, + { + "use some default params", + "alertmanager:9093", + &Config{PathPrefix: "test"}, + "http://alertmanager:9093/test/api/v2/alerts", + false, + }, + { + "use target address", + "https://alertmanager:9093/api/v1/alerts", + &Config{Scheme: "http", PathPrefix: "test"}, + "https://alertmanager:9093/api/v1/alerts", + false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + address, _, err := parseLabels(tc.target, nil, tc.cfg) + if err == nil == tc.expectedErr { + t.Fatalf("unexpected error; got %t; want %t", err != nil, tc.expectedErr) + } + if address != tc.expectedAddress { + t.Fatalf("unexpected address; got %q; want %q", address, tc.expectedAddress) + } + }) + } +} diff --git a/app/vmalert/notifier/testdata/static.good.yaml b/app/vmalert/notifier/testdata/static.good.yaml index 1d95d252d1..5a55ad3437 100644 --- a/app/vmalert/notifier/testdata/static.good.yaml +++ b/app/vmalert/notifier/testdata/static.good.yaml @@ -5,6 +5,7 @@ static_configs: - targets: - localhost:9093 - localhost:9095 + - https://localhost:9093/test/api/v2/alerts basic_auth: username: foo password: bar diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d321e9907d..368f06036c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -49,6 +49,7 @@ The sandbox cluster installation is running under the constant load generated by This also means that `datasource.queryTimeAlignment` command-line flag becomes deprecated now and will have no effect if configured. If `datasource.queryTimeAlignment` was set to `false` before, then `eval_alignment` has to be set to `false` explicitly under group. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5049). * FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): add `-rule.evalDelay` flag and `eval_delay` attribute for [Groups](https://docs.victoriametrics.com/vmalert.html#groups). The new flag and param can be used to adjust the `time` parameter for rule evaluation requests to match [intentional query delay](https://docs.victoriametrics.com/keyConcepts.html#query-latency) from the datasource. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5155). +* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): allow specifying full url in notifier static_configs target address, like `http://alertmanager:9093/test/api/v2/alerts`. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5184). * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): support data ingestion from [NewRelic infrastructure agent](https://docs.newrelic.com/docs/infrastructure/install-infrastructure-agent). See [these docs](https://docs.victoriametrics.com/Single-server-VictoriaMetrics.html#how-to-send-data-from-newrelic-agent), [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3520) and [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/4712). * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): do not exit on startup when [scrape_configs](https://docs.victoriametrics.com/sd_configs.html#scrape_configs) refer to non-existing or invalid files with auth configs, since these files may appear / updated later. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4959) and [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5153). * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): allow loading TLS certificates from HTTP and HTTPS urls by specifying these urls at `cert_file` and `key_file` options inside `tls_config` and `proxy_tls_config` sections at [http client settings](https://docs.victoriametrics.com/sd_configs.html#http-api-client-options). diff --git a/docs/vmalert.md b/docs/vmalert.md index b462a7609f..267a23d438 100644 --- a/docs/vmalert.md +++ b/docs/vmalert.md @@ -1410,8 +1410,11 @@ For example: ```yaml static_configs: - targets: + # support using full url + - 'http://alertmanager:9093/test/api/v2/alerts' + - 'https://alertmanager:9093/api/v2/alerts' + # the following target with only host:port will be used as ://localhost:9093//api/v2/alerts - localhost:9093 - - localhost:9095 consul_sd_configs: - server: localhost:8500