From 88edb3f6cfb0933b5b551cb4b1234af275c4fced Mon Sep 17 00:00:00 2001 From: Roman Khavronenko Date: Thu, 21 Jul 2022 15:59:55 +0200 Subject: [PATCH] vmalert: allow configuring custom headers per group (#2901) See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2860 Signed-off-by: hagen1778 --- app/vmalert/README.md | 10 + app/vmalert/alerting.go | 1 + app/vmalert/config/config.go | 2 + app/vmalert/config/config_test.go | 22 + .../config/testdata/dir/rules6-bad.rules | 7 + .../config/testdata/rules/rules2-good.rules | 3 +- app/vmalert/datasource/datasource.go | 1 + app/vmalert/datasource/init.go | 2 +- app/vmalert/datasource/type.go | 25 + app/vmalert/datasource/vm.go | 5 + app/vmalert/datasource/vm_test.go | 36 +- app/vmalert/group.go | 7 +- app/vmalert/manager.go | 12 + app/vmalert/recording.go | 1 + app/vmalert/web.qtpl | 7 + app/vmalert/web.qtpl.go | 657 +++++++++--------- app/vmalert/web_types.go | 2 + docs/CHANGELOG.md | 2 +- docs/vmalert.md | 10 + lib/promauth/config.go | 2 +- 20 files changed, 492 insertions(+), 322 deletions(-) create mode 100644 app/vmalert/config/testdata/dir/rules6-bad.rules diff --git a/app/vmalert/README.md b/app/vmalert/README.md index 05c3dede8..06dcb42ea 100644 --- a/app/vmalert/README.md +++ b/app/vmalert/README.md @@ -125,6 +125,16 @@ name: params: [ : [, ...]] +# Optional list of HTTP headers in form `header-name: value` +# applied for all rules requests within a group +# For example: +# headers: +# - "CustomHeader: foo" +# - "CustomHeader2: bar" +# Headers set via this param have priority over headers set via `-datasource.headers` flag. +headers: + [ , ...] + # Optional list of labels added to every rule within a group. # It has priority over the external labels. # Labels are commonly used for adding environment diff --git a/app/vmalert/alerting.go b/app/vmalert/alerting.go index 7cfe1076a..c0f3938c7 100644 --- a/app/vmalert/alerting.go +++ b/app/vmalert/alerting.go @@ -75,6 +75,7 @@ func newAlertingRule(qb datasource.QuerierBuilder, group *Group, cfg config.Rule DataSourceType: &group.Type, EvaluationInterval: group.Interval, QueryParams: group.Params, + Headers: group.Headers, }), alerts: make(map[uint64]*notifier.Alert), metrics: &alertingRuleMetrics{}, diff --git a/app/vmalert/config/config.go b/app/vmalert/config/config.go index fc50de5ee..b186b55cb 100644 --- a/app/vmalert/config/config.go +++ b/app/vmalert/config/config.go @@ -38,6 +38,8 @@ type Group struct { Checksum string // Optional HTTP URL parameters added to each rule request Params url.Values `yaml:"params"` + // Headers contains optional HTTP headers added to each rule request + Headers []datasource.Header `yaml:"headers,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` diff --git a/app/vmalert/config/config_test.go b/app/vmalert/config/config_test.go index ba2d18667..e383fe6d7 100644 --- a/app/vmalert/config/config_test.go +++ b/app/vmalert/config/config_test.go @@ -60,6 +60,10 @@ func TestParseBad(t *testing.T) { []string{"testdata/rules/rules1-bad.rules"}, "bad graphite expr", }, + { + []string{"testdata/dir/rules6-bad.rules"}, + "missing ':' in header", + }, } for _, tc := range testCases { _, err := Parse(tc.path, true, true) @@ -505,6 +509,24 @@ rules: `, ` name: TestGroup limit: 10 +rules: + - alert: foo + expr: sum by(job) (up == 1) +`) + }) + + t.Run("`headers` change", func(t *testing.T) { + f(t, ` +name: TestGroup +headers: + - "TenantID: foo" +rules: + - alert: foo + expr: sum by(job) (up == 1) +`, ` +name: TestGroup +headers: + - "TenantID: bar" rules: - alert: foo expr: sum by(job) (up == 1) diff --git a/app/vmalert/config/testdata/dir/rules6-bad.rules b/app/vmalert/config/testdata/dir/rules6-bad.rules new file mode 100644 index 000000000..98a4f1ec0 --- /dev/null +++ b/app/vmalert/config/testdata/dir/rules6-bad.rules @@ -0,0 +1,7 @@ +groups: + - name: group + headers: + - 'foobar' + rules: + - alert: rows + expr: vm_rows > 0 \ No newline at end of file diff --git a/app/vmalert/config/testdata/rules/rules2-good.rules b/app/vmalert/config/testdata/rules/rules2-good.rules index 658adacfb..94b15bea9 100644 --- a/app/vmalert/config/testdata/rules/rules2-good.rules +++ b/app/vmalert/config/testdata/rules/rules2-good.rules @@ -3,9 +3,10 @@ groups: interval: 2s concurrency: 2 limit: 1000 + headers: + - "MyHeader: foo" params: denyPartialResponse: ["true"] - extra_label: ["env=dev"] rules: - alert: Conns expr: sum(vm_tcplistener_conns) by(instance) > 1 diff --git a/app/vmalert/datasource/datasource.go b/app/vmalert/datasource/datasource.go index 5fd7f7acf..53cfd94d3 100644 --- a/app/vmalert/datasource/datasource.go +++ b/app/vmalert/datasource/datasource.go @@ -22,6 +22,7 @@ type QuerierParams struct { DataSourceType *Type EvaluationInterval time.Duration QueryParams url.Values + Headers []Header } // Metric is the basic entity which should be return by datasource diff --git a/app/vmalert/datasource/init.go b/app/vmalert/datasource/init.go index c05a42c04..fd5465bcb 100644 --- a/app/vmalert/datasource/init.go +++ b/app/vmalert/datasource/init.go @@ -15,7 +15,7 @@ var ( "E.g. http://127.0.0.1:8428 . See also -remoteRead.disablePathAppend") appendTypePrefix = flag.Bool("datasource.appendTypePrefix", false, "Whether to add type prefix to -datasource.url based on the query type. Set to true if sending different query types to the vmselect URL.") - headers = flag.String("datasource.headers", "", "Optional HTTP headers to send with each request to the corresponding -datasource.url. "+ + headers = flag.String("datasource.headers", "", "Optional HTTP extraHeaders to send with each request to the corresponding -datasource.url. "+ "For example, -datasource.headers='My-Auth:foobar' would send 'My-Auth: foobar' HTTP header with every request to the corresponding -datasource.url. "+ "Multiple headers must be delimited by '^^': -datasource.headers='header1:value1^^header2:value2'") diff --git a/app/vmalert/datasource/type.go b/app/vmalert/datasource/type.go index 975a642e4..716ebfac5 100644 --- a/app/vmalert/datasource/type.go +++ b/app/vmalert/datasource/type.go @@ -2,6 +2,7 @@ package datasource import ( "fmt" + "strings" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/graphiteql" "github.com/VictoriaMetrics/metricsql" @@ -89,3 +90,27 @@ func (t *Type) UnmarshalYAML(unmarshal func(interface{}) error) error { func (t Type) MarshalYAML() (interface{}, error) { return t.name, nil } + +// Header is a Key - Value struct for holding an HTTP header. +type Header struct { + Key string + Value string +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (h *Header) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + if err := unmarshal(&s); err != nil { + return err + } + if s == "" { + return nil + } + n := strings.IndexByte(s, ':') + if n < 0 { + return fmt.Errorf(`missing ':' in header %q; expecting "key: value" format`, s) + } + h.Key = strings.TrimSpace(s[:n]) + h.Value = strings.TrimSpace(s[n+1:]) + return nil +} diff --git a/app/vmalert/datasource/vm.go b/app/vmalert/datasource/vm.go index df415dd66..f07fec6be 100644 --- a/app/vmalert/datasource/vm.go +++ b/app/vmalert/datasource/vm.go @@ -24,6 +24,7 @@ type VMStorage struct { dataSourceType Type evaluationInterval time.Duration extraParams url.Values + extraHeaders []Header } // Clone makes clone of VMStorage, shares http client. @@ -46,6 +47,7 @@ func (s *VMStorage) ApplyParams(params QuerierParams) *VMStorage { } s.evaluationInterval = params.EvaluationInterval s.extraParams = params.QueryParams + s.extraHeaders = params.Headers return s } @@ -148,5 +150,8 @@ func (s *VMStorage) newRequestPOST() (*http.Request, error) { if s.authCfg != nil { s.authCfg.SetHeaders(req, true) } + for _, h := range s.extraHeaders { + req.Header.Set(h.Key, h.Value) + } return req, nil } diff --git a/app/vmalert/datasource/vm_test.go b/app/vmalert/datasource/vm_test.go index 1cd433cee..76cd7290b 100644 --- a/app/vmalert/datasource/vm_test.go +++ b/app/vmalert/datasource/vm_test.go @@ -487,7 +487,7 @@ func TestRequestParams(t *testing.T) { } } -func TestAuthConfig(t *testing.T) { +func TestHeaders(t *testing.T) { var testCases = []struct { name string vmFn func() *VMStorage @@ -527,6 +527,40 @@ func TestAuthConfig(t *testing.T) { checkEqualString(t, "foo", token) }, }, + { + name: "custom extraHeaders", + vmFn: func() *VMStorage { + return &VMStorage{extraHeaders: []Header{ + {Key: "Foo", Value: "bar"}, + {Key: "Baz", Value: "qux"}, + }} + }, + checkFn: func(t *testing.T, r *http.Request) { + h1 := r.Header.Get("Foo") + checkEqualString(t, "bar", h1) + h2 := r.Header.Get("Baz") + checkEqualString(t, "qux", h2) + }, + }, + { + name: "custom header overrides basic auth", + vmFn: func() *VMStorage { + cfg, err := utils.AuthConfig(utils.WithBasicAuth("foo", "bar", "")) + if err != nil { + t.Errorf("Error get auth config: %s", err) + } + return &VMStorage{ + authCfg: cfg, + extraHeaders: []Header{ + {Key: "Authorization", Value: "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="}, + }} + }, + checkFn: func(t *testing.T, r *http.Request) { + u, p, _ := r.BasicAuth() + checkEqualString(t, "Aladdin", u) + checkEqualString(t, "open sesame", p) + }, + }, } for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { diff --git a/app/vmalert/group.go b/app/vmalert/group.go index ac8e712bd..cafdb2d91 100644 --- a/app/vmalert/group.go +++ b/app/vmalert/group.go @@ -35,8 +35,9 @@ type Group struct { Checksum string LastEvaluation time.Time - Labels map[string]string - Params url.Values + Labels map[string]string + Params url.Values + Headers []datasource.Header doneCh chan struct{} finishedCh chan struct{} @@ -96,6 +97,7 @@ func newGroup(cfg config.Group, qb datasource.QuerierBuilder, defaultInterval ti Concurrency: cfg.Concurrency, Checksum: cfg.Checksum, Params: cfg.Params, + Headers: cfg.Headers, Labels: cfg.Labels, doneCh: make(chan struct{}), @@ -217,6 +219,7 @@ func (g *Group) updateWith(newGroup *Group) error { g.Type = newGroup.Type g.Concurrency = newGroup.Concurrency g.Params = newGroup.Params + g.Headers = newGroup.Headers g.Labels = newGroup.Labels g.Limit = newGroup.Limit g.Checksum = newGroup.Checksum diff --git a/app/vmalert/manager.go b/app/vmalert/manager.go index 3ab70c6f4..e3f3a6452 100644 --- a/app/vmalert/manager.go +++ b/app/vmalert/manager.go @@ -170,6 +170,7 @@ func (g *Group) toAPI() APIGroup { LastEvaluation: g.LastEvaluation, Concurrency: g.Concurrency, Params: urlValuesToStrings(g.Params), + Headers: headersToStrings(g.Headers), Labels: g.Labels, } for _, r := range g.Rules { @@ -198,3 +199,14 @@ func urlValuesToStrings(values url.Values) []string { } return res } + +func headersToStrings(headers []datasource.Header) []string { + if len(headers) < 1 { + return nil + } + var res []string + for _, h := range headers { + res = append(res, fmt.Sprintf("%s: %s", h.Key, h.Value)) + } + return res +} diff --git a/app/vmalert/recording.go b/app/vmalert/recording.go index a22285906..ab5eb270d 100644 --- a/app/vmalert/recording.go +++ b/app/vmalert/recording.go @@ -73,6 +73,7 @@ func newRecordingRule(qb datasource.QuerierBuilder, group *Group, cfg config.Rul DataSourceType: &group.Type, EvaluationInterval: group.Interval, QueryParams: group.Params, + Headers: group.Headers, }), } diff --git a/app/vmalert/web.qtpl b/app/vmalert/web.qtpl index 7e5db0a5c..693186550 100644 --- a/app/vmalert/web.qtpl +++ b/app/vmalert/web.qtpl @@ -57,6 +57,13 @@ {% endfor %} {% endif %} + {% if len(g.Headers) > 0 %} +
Extra headers + {% for _, header := range g.Headers %} + {%s header %} + {% endfor %} +
+ {% endif %}
diff --git a/app/vmalert/web.qtpl.go b/app/vmalert/web.qtpl.go index aa73307c6..6f67cd551 100644 --- a/app/vmalert/web.qtpl.go +++ b/app/vmalert/web.qtpl.go @@ -232,11 +232,38 @@ func StreamListGroups(qw422016 *qt422016.Writer, r *http.Request, groups []APIGr } //line app/vmalert/web.qtpl:59 qw422016.N().S(` + `) +//line app/vmalert/web.qtpl:60 + if len(g.Headers) > 0 { +//line app/vmalert/web.qtpl:60 + qw422016.N().S(` +
Extra headers + `) +//line app/vmalert/web.qtpl:62 + for _, header := range g.Headers { +//line app/vmalert/web.qtpl:62 + qw422016.N().S(` + `) +//line app/vmalert/web.qtpl:63 + qw422016.E().S(header) +//line app/vmalert/web.qtpl:63 + qw422016.N().S(` + `) +//line app/vmalert/web.qtpl:64 + } +//line app/vmalert/web.qtpl:64 + qw422016.N().S(` +
+ `) +//line app/vmalert/web.qtpl:66 + } +//line app/vmalert/web.qtpl:66 + qw422016.N().S(`
@@ -248,262 +275,262 @@ func StreamListGroups(qw422016 *qt422016.Writer, r *http.Request, groups []APIGr `) -//line app/vmalert/web.qtpl:71 +//line app/vmalert/web.qtpl:78 for _, r := range g.Rules { -//line app/vmalert/web.qtpl:71 +//line app/vmalert/web.qtpl:78 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:104 +//line app/vmalert/web.qtpl:111 } -//line app/vmalert/web.qtpl:104 +//line app/vmalert/web.qtpl:111 qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:76 +//line app/vmalert/web.qtpl:83 if r.Type == "alerting" { -//line app/vmalert/web.qtpl:76 +//line app/vmalert/web.qtpl:83 qw422016.N().S(` alert: `) -//line app/vmalert/web.qtpl:77 +//line app/vmalert/web.qtpl:84 qw422016.E().S(r.Name) -//line app/vmalert/web.qtpl:77 +//line app/vmalert/web.qtpl:84 qw422016.N().S(` (for: `) -//line app/vmalert/web.qtpl:77 +//line app/vmalert/web.qtpl:84 qw422016.E().V(r.Duration) -//line app/vmalert/web.qtpl:77 +//line app/vmalert/web.qtpl:84 qw422016.N().S(` seconds) `) -//line app/vmalert/web.qtpl:78 +//line app/vmalert/web.qtpl:85 } else { -//line app/vmalert/web.qtpl:78 +//line app/vmalert/web.qtpl:85 qw422016.N().S(` record: `) -//line app/vmalert/web.qtpl:79 +//line app/vmalert/web.qtpl:86 qw422016.E().S(r.Name) -//line app/vmalert/web.qtpl:79 +//line app/vmalert/web.qtpl:86 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:80 +//line app/vmalert/web.qtpl:87 } -//line app/vmalert/web.qtpl:80 +//line app/vmalert/web.qtpl:87 qw422016.N().S(`
`)
-//line app/vmalert/web.qtpl:83
+//line app/vmalert/web.qtpl:90
 				qw422016.E().S(r.Query)
-//line app/vmalert/web.qtpl:83
+//line app/vmalert/web.qtpl:90
 				qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:86 +//line app/vmalert/web.qtpl:93 if len(r.Labels) > 0 { -//line app/vmalert/web.qtpl:86 +//line app/vmalert/web.qtpl:93 qw422016.N().S(` Labels:`) -//line app/vmalert/web.qtpl:86 +//line app/vmalert/web.qtpl:93 } -//line app/vmalert/web.qtpl:86 +//line app/vmalert/web.qtpl:93 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:87 +//line app/vmalert/web.qtpl:94 for k, v := range r.Labels { -//line app/vmalert/web.qtpl:87 +//line app/vmalert/web.qtpl:94 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:88 +//line app/vmalert/web.qtpl:95 qw422016.E().S(k) -//line app/vmalert/web.qtpl:88 +//line app/vmalert/web.qtpl:95 qw422016.N().S(`=`) -//line app/vmalert/web.qtpl:88 +//line app/vmalert/web.qtpl:95 qw422016.E().S(v) -//line app/vmalert/web.qtpl:88 +//line app/vmalert/web.qtpl:95 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:89 +//line app/vmalert/web.qtpl:96 } -//line app/vmalert/web.qtpl:89 +//line app/vmalert/web.qtpl:96 qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:91 +//line app/vmalert/web.qtpl:98 if r.LastError != "" { -//line app/vmalert/web.qtpl:91 +//line app/vmalert/web.qtpl:98 qw422016.N().S(`
Error:
`) -//line app/vmalert/web.qtpl:95 +//line app/vmalert/web.qtpl:102 qw422016.E().S(r.LastError) -//line app/vmalert/web.qtpl:95 +//line app/vmalert/web.qtpl:102 qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:98 +//line app/vmalert/web.qtpl:105 } -//line app/vmalert/web.qtpl:98 +//line app/vmalert/web.qtpl:105 qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:101 +//line app/vmalert/web.qtpl:108 qw422016.N().D(r.LastSamples) -//line app/vmalert/web.qtpl:101 +//line app/vmalert/web.qtpl:108 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:102 +//line app/vmalert/web.qtpl:109 qw422016.N().FPrec(time.Since(r.LastEvaluation).Seconds(), 3) -//line app/vmalert/web.qtpl:102 +//line app/vmalert/web.qtpl:109 qw422016.N().S(`s ago
`) -//line app/vmalert/web.qtpl:108 +//line app/vmalert/web.qtpl:115 } -//line app/vmalert/web.qtpl:108 +//line app/vmalert/web.qtpl:115 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:110 +//line app/vmalert/web.qtpl:117 } else { -//line app/vmalert/web.qtpl:110 +//line app/vmalert/web.qtpl:117 qw422016.N().S(`

No items...

`) -//line app/vmalert/web.qtpl:114 +//line app/vmalert/web.qtpl:121 } -//line app/vmalert/web.qtpl:114 +//line app/vmalert/web.qtpl:121 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:116 +//line app/vmalert/web.qtpl:123 tpl.StreamFooter(qw422016, r) -//line app/vmalert/web.qtpl:116 +//line app/vmalert/web.qtpl:123 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 } -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 func WriteListGroups(qq422016 qtio422016.Writer, r *http.Request, groups []APIGroup) { -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 StreamListGroups(qw422016, r, groups) -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 qt422016.ReleaseWriter(qw422016) -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 } -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 func ListGroups(r *http.Request, groups []APIGroup) string { -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 WriteListGroups(qb422016, r, groups) -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 qs422016 := string(qb422016.B) -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 return qs422016 -//line app/vmalert/web.qtpl:118 +//line app/vmalert/web.qtpl:125 } -//line app/vmalert/web.qtpl:121 +//line app/vmalert/web.qtpl:128 func StreamListAlerts(qw422016 *qt422016.Writer, r *http.Request, groupAlerts []GroupAlerts) { -//line app/vmalert/web.qtpl:121 +//line app/vmalert/web.qtpl:128 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:122 +//line app/vmalert/web.qtpl:129 prefix := utils.Prefix(r.URL.Path) -//line app/vmalert/web.qtpl:122 +//line app/vmalert/web.qtpl:129 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:123 +//line app/vmalert/web.qtpl:130 tpl.StreamHeader(qw422016, r, navItems, "Alerts") -//line app/vmalert/web.qtpl:123 +//line app/vmalert/web.qtpl:130 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:124 +//line app/vmalert/web.qtpl:131 if len(groupAlerts) > 0 { -//line app/vmalert/web.qtpl:124 +//line app/vmalert/web.qtpl:131 qw422016.N().S(` Collapse All Expand All `) -//line app/vmalert/web.qtpl:127 +//line app/vmalert/web.qtpl:134 for _, ga := range groupAlerts { -//line app/vmalert/web.qtpl:127 +//line app/vmalert/web.qtpl:134 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:128 +//line app/vmalert/web.qtpl:135 g := ga.Group -//line app/vmalert/web.qtpl:128 +//line app/vmalert/web.qtpl:135 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:137 +//line app/vmalert/web.qtpl:144 var keys []string alertsByRule := make(map[string][]*APIAlert) for _, alert := range ga.Alerts { @@ -514,20 +541,20 @@ func StreamListAlerts(qw422016 *qt422016.Writer, r *http.Request, groupAlerts [] } sort.Strings(keys) -//line app/vmalert/web.qtpl:146 +//line app/vmalert/web.qtpl:153 qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:148 +//line app/vmalert/web.qtpl:155 for _, ruleID := range keys { -//line app/vmalert/web.qtpl:148 +//line app/vmalert/web.qtpl:155 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:150 +//line app/vmalert/web.qtpl:157 defaultAR := alertsByRule[ruleID][0] var labelKeys []string for k := range defaultAR.Labels { @@ -535,28 +562,28 @@ func StreamListAlerts(qw422016 *qt422016.Writer, r *http.Request, groupAlerts [] } sort.Strings(labelKeys) -//line app/vmalert/web.qtpl:156 +//line app/vmalert/web.qtpl:163 qw422016.N().S(`
alert: `) -//line app/vmalert/web.qtpl:158 +//line app/vmalert/web.qtpl:165 qw422016.E().S(defaultAR.Name) -//line app/vmalert/web.qtpl:158 +//line app/vmalert/web.qtpl:165 qw422016.N().S(` (`) -//line app/vmalert/web.qtpl:158 +//line app/vmalert/web.qtpl:165 qw422016.N().D(len(alertsByRule[ruleID])) -//line app/vmalert/web.qtpl:158 +//line app/vmalert/web.qtpl:165 qw422016.N().S(`) | Source
expr:
`)
-//line app/vmalert/web.qtpl:161
+//line app/vmalert/web.qtpl:168
 				qw422016.E().S(defaultAR.Expression)
-//line app/vmalert/web.qtpl:161
+//line app/vmalert/web.qtpl:168
 				qw422016.N().S(`
@@ -570,204 +597,204 @@ func StreamListAlerts(qw422016 *qt422016.Writer, r *http.Request, groupAlerts [] `) -//line app/vmalert/web.qtpl:173 +//line app/vmalert/web.qtpl:180 for _, ar := range alertsByRule[ruleID] { -//line app/vmalert/web.qtpl:173 +//line app/vmalert/web.qtpl:180 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:190 +//line app/vmalert/web.qtpl:197 } -//line app/vmalert/web.qtpl:190 +//line app/vmalert/web.qtpl:197 qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:176 +//line app/vmalert/web.qtpl:183 for _, k := range labelKeys { -//line app/vmalert/web.qtpl:176 +//line app/vmalert/web.qtpl:183 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:177 +//line app/vmalert/web.qtpl:184 qw422016.E().S(k) -//line app/vmalert/web.qtpl:177 +//line app/vmalert/web.qtpl:184 qw422016.N().S(`=`) -//line app/vmalert/web.qtpl:177 +//line app/vmalert/web.qtpl:184 qw422016.E().S(ar.Labels[k]) -//line app/vmalert/web.qtpl:177 +//line app/vmalert/web.qtpl:184 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:178 +//line app/vmalert/web.qtpl:185 } -//line app/vmalert/web.qtpl:178 +//line app/vmalert/web.qtpl:185 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:180 +//line app/vmalert/web.qtpl:187 streambadgeState(qw422016, ar.State) -//line app/vmalert/web.qtpl:180 +//line app/vmalert/web.qtpl:187 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:182 +//line app/vmalert/web.qtpl:189 qw422016.E().S(ar.ActiveAt.Format("2006-01-02T15:04:05Z07:00")) -//line app/vmalert/web.qtpl:182 +//line app/vmalert/web.qtpl:189 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:183 +//line app/vmalert/web.qtpl:190 if ar.Restored { -//line app/vmalert/web.qtpl:183 +//line app/vmalert/web.qtpl:190 streambadgeRestored(qw422016) -//line app/vmalert/web.qtpl:183 +//line app/vmalert/web.qtpl:190 } -//line app/vmalert/web.qtpl:183 +//line app/vmalert/web.qtpl:190 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:185 +//line app/vmalert/web.qtpl:192 qw422016.E().S(ar.Value) -//line app/vmalert/web.qtpl:185 +//line app/vmalert/web.qtpl:192 qw422016.N().S(` Details
`) -//line app/vmalert/web.qtpl:193 +//line app/vmalert/web.qtpl:200 } -//line app/vmalert/web.qtpl:193 +//line app/vmalert/web.qtpl:200 qw422016.N().S(`

`) -//line app/vmalert/web.qtpl:196 +//line app/vmalert/web.qtpl:203 } -//line app/vmalert/web.qtpl:196 +//line app/vmalert/web.qtpl:203 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:198 +//line app/vmalert/web.qtpl:205 } else { -//line app/vmalert/web.qtpl:198 +//line app/vmalert/web.qtpl:205 qw422016.N().S(`

No items...

`) -//line app/vmalert/web.qtpl:202 +//line app/vmalert/web.qtpl:209 } -//line app/vmalert/web.qtpl:202 +//line app/vmalert/web.qtpl:209 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:204 +//line app/vmalert/web.qtpl:211 tpl.StreamFooter(qw422016, r) -//line app/vmalert/web.qtpl:204 +//line app/vmalert/web.qtpl:211 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 } -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 func WriteListAlerts(qq422016 qtio422016.Writer, r *http.Request, groupAlerts []GroupAlerts) { -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 StreamListAlerts(qw422016, r, groupAlerts) -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 qt422016.ReleaseWriter(qw422016) -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 } -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 func ListAlerts(r *http.Request, groupAlerts []GroupAlerts) string { -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 WriteListAlerts(qb422016, r, groupAlerts) -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 qs422016 := string(qb422016.B) -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 return qs422016 -//line app/vmalert/web.qtpl:206 +//line app/vmalert/web.qtpl:213 } -//line app/vmalert/web.qtpl:208 +//line app/vmalert/web.qtpl:215 func StreamListTargets(qw422016 *qt422016.Writer, r *http.Request, targets map[notifier.TargetType][]notifier.Target) { -//line app/vmalert/web.qtpl:208 +//line app/vmalert/web.qtpl:215 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:209 +//line app/vmalert/web.qtpl:216 tpl.StreamHeader(qw422016, r, navItems, "Notifiers") -//line app/vmalert/web.qtpl:209 +//line app/vmalert/web.qtpl:216 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:210 +//line app/vmalert/web.qtpl:217 if len(targets) > 0 { -//line app/vmalert/web.qtpl:210 +//line app/vmalert/web.qtpl:217 qw422016.N().S(` Collapse All Expand All `) -//line app/vmalert/web.qtpl:215 +//line app/vmalert/web.qtpl:222 var keys []string for key := range targets { keys = append(keys, string(key)) } sort.Strings(keys) -//line app/vmalert/web.qtpl:220 +//line app/vmalert/web.qtpl:227 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:222 +//line app/vmalert/web.qtpl:229 for i := range keys { -//line app/vmalert/web.qtpl:222 +//line app/vmalert/web.qtpl:229 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:223 +//line app/vmalert/web.qtpl:230 typeK, ns := keys[i], targets[notifier.TargetType(keys[i])] count := len(ns) -//line app/vmalert/web.qtpl:225 +//line app/vmalert/web.qtpl:232 qw422016.N().S(`
@@ -778,119 +805,119 @@ func StreamListTargets(qw422016 *qt422016.Writer, r *http.Request, targets map[n `) -//line app/vmalert/web.qtpl:239 +//line app/vmalert/web.qtpl:246 for _, n := range ns { -//line app/vmalert/web.qtpl:239 +//line app/vmalert/web.qtpl:246 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:248 +//line app/vmalert/web.qtpl:255 } -//line app/vmalert/web.qtpl:248 +//line app/vmalert/web.qtpl:255 qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:242 +//line app/vmalert/web.qtpl:249 for _, l := range n.Labels { -//line app/vmalert/web.qtpl:242 +//line app/vmalert/web.qtpl:249 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:243 +//line app/vmalert/web.qtpl:250 qw422016.E().S(l.Name) -//line app/vmalert/web.qtpl:243 +//line app/vmalert/web.qtpl:250 qw422016.N().S(`=`) -//line app/vmalert/web.qtpl:243 +//line app/vmalert/web.qtpl:250 qw422016.E().S(l.Value) -//line app/vmalert/web.qtpl:243 +//line app/vmalert/web.qtpl:250 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:244 +//line app/vmalert/web.qtpl:251 } -//line app/vmalert/web.qtpl:244 +//line app/vmalert/web.qtpl:251 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:246 +//line app/vmalert/web.qtpl:253 qw422016.E().S(n.Notifier.Addr()) -//line app/vmalert/web.qtpl:246 +//line app/vmalert/web.qtpl:253 qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:252 +//line app/vmalert/web.qtpl:259 } -//line app/vmalert/web.qtpl:252 +//line app/vmalert/web.qtpl:259 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:254 +//line app/vmalert/web.qtpl:261 } else { -//line app/vmalert/web.qtpl:254 +//line app/vmalert/web.qtpl:261 qw422016.N().S(`

No items...

`) -//line app/vmalert/web.qtpl:258 +//line app/vmalert/web.qtpl:265 } -//line app/vmalert/web.qtpl:258 +//line app/vmalert/web.qtpl:265 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:260 +//line app/vmalert/web.qtpl:267 tpl.StreamFooter(qw422016, r) -//line app/vmalert/web.qtpl:260 +//line app/vmalert/web.qtpl:267 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 } -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 func WriteListTargets(qq422016 qtio422016.Writer, r *http.Request, targets map[notifier.TargetType][]notifier.Target) { -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 StreamListTargets(qw422016, r, targets) -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 qt422016.ReleaseWriter(qw422016) -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 } -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 func ListTargets(r *http.Request, targets map[notifier.TargetType][]notifier.Target) string { -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 WriteListTargets(qb422016, r, targets) -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 qs422016 := string(qb422016.B) -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 return qs422016 -//line app/vmalert/web.qtpl:262 +//line app/vmalert/web.qtpl:269 } -//line app/vmalert/web.qtpl:264 +//line app/vmalert/web.qtpl:271 func StreamAlert(qw422016 *qt422016.Writer, r *http.Request, alert *APIAlert) { -//line app/vmalert/web.qtpl:264 +//line app/vmalert/web.qtpl:271 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:265 +//line app/vmalert/web.qtpl:272 prefix := utils.Prefix(r.URL.Path) -//line app/vmalert/web.qtpl:265 +//line app/vmalert/web.qtpl:272 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:266 +//line app/vmalert/web.qtpl:273 tpl.StreamHeader(qw422016, r, navItems, "") -//line app/vmalert/web.qtpl:266 +//line app/vmalert/web.qtpl:273 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:268 +//line app/vmalert/web.qtpl:275 var labelKeys []string for k := range alert.Labels { labelKeys = append(labelKeys, k) @@ -903,28 +930,28 @@ func StreamAlert(qw422016 *qt422016.Writer, r *http.Request, alert *APIAlert) { } sort.Strings(annotationKeys) -//line app/vmalert/web.qtpl:279 +//line app/vmalert/web.qtpl:286 qw422016.N().S(`
`) -//line app/vmalert/web.qtpl:280 +//line app/vmalert/web.qtpl:287 qw422016.E().S(alert.Name) -//line app/vmalert/web.qtpl:280 +//line app/vmalert/web.qtpl:287 qw422016.N().S(``) -//line app/vmalert/web.qtpl:280 +//line app/vmalert/web.qtpl:287 qw422016.E().S(alert.State) -//line app/vmalert/web.qtpl:280 +//line app/vmalert/web.qtpl:287 qw422016.N().S(`
@@ -933,9 +960,9 @@ func StreamAlert(qw422016 *qt422016.Writer, r *http.Request, alert *APIAlert) {
`) -//line app/vmalert/web.qtpl:287 +//line app/vmalert/web.qtpl:294 qw422016.E().S(alert.ActiveAt.Format("2006-01-02T15:04:05Z07:00")) -//line app/vmalert/web.qtpl:287 +//line app/vmalert/web.qtpl:294 qw422016.N().S(`
@@ -947,9 +974,9 @@ func StreamAlert(qw422016 *qt422016.Writer, r *http.Request, alert *APIAlert) {
`)
-//line app/vmalert/web.qtpl:297
+//line app/vmalert/web.qtpl:304
 	qw422016.E().S(alert.Expression)
-//line app/vmalert/web.qtpl:297
+//line app/vmalert/web.qtpl:304
 	qw422016.N().S(`
@@ -961,23 +988,23 @@ func StreamAlert(qw422016 *qt422016.Writer, r *http.Request, alert *APIAlert) {
`) -//line app/vmalert/web.qtpl:307 +//line app/vmalert/web.qtpl:314 for _, k := range labelKeys { -//line app/vmalert/web.qtpl:307 +//line app/vmalert/web.qtpl:314 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:308 +//line app/vmalert/web.qtpl:315 qw422016.E().S(k) -//line app/vmalert/web.qtpl:308 +//line app/vmalert/web.qtpl:315 qw422016.N().S(`=`) -//line app/vmalert/web.qtpl:308 +//line app/vmalert/web.qtpl:315 qw422016.E().S(alert.Labels[k]) -//line app/vmalert/web.qtpl:308 +//line app/vmalert/web.qtpl:315 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:309 +//line app/vmalert/web.qtpl:316 } -//line app/vmalert/web.qtpl:309 +//line app/vmalert/web.qtpl:316 qw422016.N().S(`
@@ -989,24 +1016,24 @@ func StreamAlert(qw422016 *qt422016.Writer, r *http.Request, alert *APIAlert) {
`) -//line app/vmalert/web.qtpl:319 +//line app/vmalert/web.qtpl:326 for _, k := range annotationKeys { -//line app/vmalert/web.qtpl:319 +//line app/vmalert/web.qtpl:326 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:320 +//line app/vmalert/web.qtpl:327 qw422016.E().S(k) -//line app/vmalert/web.qtpl:320 +//line app/vmalert/web.qtpl:327 qw422016.N().S(`:

`) -//line app/vmalert/web.qtpl:321 +//line app/vmalert/web.qtpl:328 qw422016.E().S(alert.Annotations[k]) -//line app/vmalert/web.qtpl:321 +//line app/vmalert/web.qtpl:328 qw422016.N().S(`

`) -//line app/vmalert/web.qtpl:322 +//line app/vmalert/web.qtpl:329 } -//line app/vmalert/web.qtpl:322 +//line app/vmalert/web.qtpl:329 qw422016.N().S(`
@@ -1018,17 +1045,17 @@ func StreamAlert(qw422016 *qt422016.Writer, r *http.Request, alert *APIAlert) { @@ -1040,132 +1067,132 @@ func StreamAlert(qw422016 *qt422016.Writer, r *http.Request, alert *APIAlert) { `) -//line app/vmalert/web.qtpl:346 +//line app/vmalert/web.qtpl:353 tpl.StreamFooter(qw422016, r) -//line app/vmalert/web.qtpl:346 +//line app/vmalert/web.qtpl:353 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 } -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 func WriteAlert(qq422016 qtio422016.Writer, r *http.Request, alert *APIAlert) { -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 StreamAlert(qw422016, r, alert) -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 qt422016.ReleaseWriter(qw422016) -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 } -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 func Alert(r *http.Request, alert *APIAlert) string { -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 WriteAlert(qb422016, r, alert) -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 qs422016 := string(qb422016.B) -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 return qs422016 -//line app/vmalert/web.qtpl:348 +//line app/vmalert/web.qtpl:355 } -//line app/vmalert/web.qtpl:350 +//line app/vmalert/web.qtpl:357 func streambadgeState(qw422016 *qt422016.Writer, state string) { -//line app/vmalert/web.qtpl:350 +//line app/vmalert/web.qtpl:357 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:352 +//line app/vmalert/web.qtpl:359 badgeClass := "bg-warning text-dark" if state == "firing" { badgeClass = "bg-danger" } -//line app/vmalert/web.qtpl:356 +//line app/vmalert/web.qtpl:363 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:357 +//line app/vmalert/web.qtpl:364 qw422016.E().S(state) -//line app/vmalert/web.qtpl:357 +//line app/vmalert/web.qtpl:364 qw422016.N().S(` `) -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 } -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 func writebadgeState(qq422016 qtio422016.Writer, state string) { -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 streambadgeState(qw422016, state) -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 qt422016.ReleaseWriter(qw422016) -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 } -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 func badgeState(state string) string { -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 writebadgeState(qb422016, state) -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 qs422016 := string(qb422016.B) -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 return qs422016 -//line app/vmalert/web.qtpl:358 +//line app/vmalert/web.qtpl:365 } -//line app/vmalert/web.qtpl:360 +//line app/vmalert/web.qtpl:367 func streambadgeRestored(qw422016 *qt422016.Writer) { -//line app/vmalert/web.qtpl:360 +//line app/vmalert/web.qtpl:367 qw422016.N().S(` restored `) -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 } -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 func writebadgeRestored(qq422016 qtio422016.Writer) { -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 qw422016 := qt422016.AcquireWriter(qq422016) -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 streambadgeRestored(qw422016) -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 qt422016.ReleaseWriter(qw422016) -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 } -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 func badgeRestored() string { -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 qb422016 := qt422016.AcquireByteBuffer() -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 writebadgeRestored(qb422016) -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 qs422016 := string(qb422016.B) -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 qt422016.ReleaseByteBuffer(qb422016) -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 return qs422016 -//line app/vmalert/web.qtpl:362 +//line app/vmalert/web.qtpl:369 } diff --git a/app/vmalert/web_types.go b/app/vmalert/web_types.go index b2e150807..27789c897 100644 --- a/app/vmalert/web_types.go +++ b/app/vmalert/web_types.go @@ -70,6 +70,8 @@ type APIGroup struct { Concurrency int `json:"concurrency"` // Params contains HTTP URL parameters added to each Rule's request Params []string `json:"params,omitempty"` + // Headers contains HTTP headers added to each Rule's request + Headers []string `json:"headers,omitempty"` // Labels is a set of label value pairs, that will be added to every rule. Labels map[string]string `json:"labels,omitempty"` } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bcb247f3e..228b371c7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -15,7 +15,7 @@ The following tip changes can be tested by building VictoriaMetrics components f ## tip -* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): allow configuring additional headers for `datasource.url`, `remoteWrite.url` and `remoteRead.url` URLs. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2860) for details. +* FEATURE: [vmalert](https://docs.victoriametrics.com/vmalert.html): allow configuring additional headers for `datasource.url`, `remoteWrite.url` and `remoteRead.url` URLs. Headers also can be set on group level via `headers` param. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2860) for details. * FEATURE: [MetricsQL](https://docs.victoriametrics.com/MetricsQL.html): execute left and right sides of certain operations in parallel. For example, `q1 or q2`, `aggr_func(q1) q2`, `q1 aggr_func(q1)`. This may improve query performance if VictoriaMetrics has enough free resources for parallel processing of both sides of the operation. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2886). * FEATURE: [vmauth](https://docs.victoriametrics.com/vmagent.html): allow duplicate username records with different passwords at configuration file. It should allow password rotation without username change. diff --git a/docs/vmalert.md b/docs/vmalert.md index 18346e9ad..79c31e418 100644 --- a/docs/vmalert.md +++ b/docs/vmalert.md @@ -129,6 +129,16 @@ name: params: [ : [, ...]] +# Optional list of HTTP headers in form `header-name: value` +# applied for all rules requests within a group +# For example: +# headers: +# - "CustomHeader: foo" +# - "CustomHeader2: bar" +# Headers set via this param have priority over headers set via `-datasource.headers` flag. +headers: + [ , ...] + # Optional list of labels added to every rule within a group. # It has priority over the external labels. # Labels are commonly used for adding environment diff --git a/lib/promauth/config.go b/lib/promauth/config.go index f5f5e8baa..cef24202a 100644 --- a/lib/promauth/config.go +++ b/lib/promauth/config.go @@ -308,7 +308,7 @@ func (ac *Config) HeadersNoAuthString() string { return strings.Join(a, "") } -// SetHeaders sets the configuted ac headers to req. +// SetHeaders sets the configured ac headers to req. func (ac *Config) SetHeaders(req *http.Request, setAuthHeader bool) { reqHeaders := req.Header for _, h := range ac.headers {