mirror of
https://github.com/grafana/grafana.git
synced 2024-11-25 18:30:41 -06:00
Expand the value string in annotations and labels of alerts (#37051)
This commit makes it possible to use the value string in annotations and labels for alerts with "{{ $value }}"
This commit is contained in:
parent
ad1f792b8b
commit
2f4c893cf3
@ -109,8 +109,9 @@ The following template variables are available when expanding annotations and la
|
||||
|
||||
| Name | Description |
|
||||
| ------- | --------------- |
|
||||
| $labels | Labels contains the labels from the query or condition. For example, `{{ $labels.instance }}` and `{{ $labels.job }}`. |
|
||||
| $values | Values contains the values of all reduce and math expressions that were evaluated for this alert rule. For example, `{{ $values.A }}`, `{{ $values.A.Labels }}` and `{{ $values.A.Value }}` where `A` is the `refID` of the expression. |
|
||||
| $labels | The labels from the query or condition. For example, `{{ $labels.instance }}` and `{{ $labels.job }}`. |
|
||||
| $values | The values of all reduce and math expressions that were evaluated for this alert rule. For example, `{{ $values.A }}`, `{{ $values.A.Labels }}` and `{{ $values.A.Value }}` where `A` is the `refID` of the expression. |
|
||||
| $value | The value string of the alert instance. For example, `[ var='A' labels={instance=foo} value=10 ]`. |
|
||||
|
||||
## Preview alerts
|
||||
|
||||
|
@ -39,7 +39,7 @@ func (c *cache) getOrCreate(alertRule *ngModels.AlertRule, result eval.Result) *
|
||||
// clone the labels so we don't change eval.Result
|
||||
labels := result.Instance.Copy()
|
||||
attachRuleLabels(labels, alertRule)
|
||||
ruleLabels, annotations := c.expandRuleLabelsAndAnnotations(alertRule, labels, result.Values)
|
||||
ruleLabels, annotations := c.expandRuleLabelsAndAnnotations(alertRule, labels, result)
|
||||
|
||||
// if duplicate labels exist, alertRule label will take precedence
|
||||
lbs := mergeLabels(ruleLabels, result.Instance)
|
||||
@ -88,11 +88,11 @@ func attachRuleLabels(m map[string]string, alertRule *ngModels.AlertRule) {
|
||||
m[prometheusModel.AlertNameLabel] = alertRule.Title
|
||||
}
|
||||
|
||||
func (c *cache) expandRuleLabelsAndAnnotations(alertRule *ngModels.AlertRule, labels map[string]string, values map[string]eval.NumberValueCapture) (map[string]string, map[string]string) {
|
||||
func (c *cache) expandRuleLabelsAndAnnotations(alertRule *ngModels.AlertRule, labels map[string]string, alertInstance eval.Result) (map[string]string, map[string]string) {
|
||||
expand := func(original map[string]string) map[string]string {
|
||||
expanded := make(map[string]string, len(original))
|
||||
for k, v := range original {
|
||||
ev, err := expandTemplate(alertRule.Title, v, labels, values)
|
||||
ev, err := expandTemplate(alertRule.Title, v, labels, alertInstance)
|
||||
expanded[k] = ev
|
||||
if err != nil {
|
||||
c.log.Error("error in expanding template", "name", k, "value", v, "err", err.Error())
|
||||
@ -122,9 +122,9 @@ func (v templateCaptureValue) String() string {
|
||||
return "null"
|
||||
}
|
||||
|
||||
func expandTemplate(name, text string, labels map[string]string, values map[string]eval.NumberValueCapture) (result string, resultErr error) {
|
||||
func expandTemplate(name, text string, labels map[string]string, alertInstance eval.Result) (result string, resultErr error) {
|
||||
name = "__alert_" + name
|
||||
text = "{{- $labels := .Labels -}}{{- $values := .Values -}}" + text
|
||||
text = "{{- $labels := .Labels -}}{{- $values := .Values -}}{{- $value := .Value -}}" + text
|
||||
// It'd better to have no alert description than to kill the whole process
|
||||
// if there's a bug in the template.
|
||||
defer func() {
|
||||
@ -145,11 +145,12 @@ func expandTemplate(name, text string, labels map[string]string, values map[stri
|
||||
if err := tmpl.Execute(&buffer, struct {
|
||||
Labels map[string]string
|
||||
Values map[string]templateCaptureValue
|
||||
Value string
|
||||
}{
|
||||
Labels: labels,
|
||||
Values: func() map[string]templateCaptureValue {
|
||||
m := make(map[string]templateCaptureValue)
|
||||
for k, v := range values {
|
||||
for k, v := range alertInstance.Values {
|
||||
m[k] = templateCaptureValue{
|
||||
Labels: v.Labels,
|
||||
Value: v.Value,
|
||||
@ -157,6 +158,7 @@ func expandTemplate(name, text string, labels map[string]string, values map[stri
|
||||
}
|
||||
return m
|
||||
}(),
|
||||
Value: alertInstance.EvaluationString,
|
||||
}); err != nil {
|
||||
return "", fmt.Errorf("error executing template %v: %s", name, err.Error())
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
ptr "github.com/xorcare/pointer"
|
||||
)
|
||||
|
||||
@ -32,3 +36,65 @@ func TestTemplateCaptureValueStringer(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandTemplate(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
text string
|
||||
alertInstance eval.Result
|
||||
labels data.Labels
|
||||
expected string
|
||||
expectedError error
|
||||
}{{
|
||||
name: "instance labels are expanded into $labels",
|
||||
text: "{{ $labels.instance }} is down",
|
||||
labels: data.Labels{"instance": "foo"},
|
||||
expected: "foo is down",
|
||||
}, {
|
||||
name: "missing instance label returns error",
|
||||
text: "{{ $labels.instance }} is down",
|
||||
labels: data.Labels{},
|
||||
expectedError: errors.New("error executing template __alert_test: template: __alert_test:1:86: executing \"__alert_test\" at <$labels.instance>: map has no entry for key \"instance\""),
|
||||
}, {
|
||||
name: "values are expanded into $values",
|
||||
text: "{{ $values.A.Labels.instance }} has value {{ $values.A }}",
|
||||
alertInstance: eval.Result{
|
||||
Values: map[string]eval.NumberValueCapture{
|
||||
"A": {
|
||||
Var: "A",
|
||||
Labels: data.Labels{"instance": "foo"},
|
||||
Value: ptr.Float64(10),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: "foo has value 10",
|
||||
}, {
|
||||
name: "missing label in $values returns error",
|
||||
text: "{{ $values.A.Labels.instance }} has value {{ $values.A }}",
|
||||
alertInstance: eval.Result{
|
||||
Values: map[string]eval.NumberValueCapture{
|
||||
"A": {
|
||||
Var: "A",
|
||||
Labels: data.Labels{},
|
||||
Value: ptr.Float64(10),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: errors.New("error executing template __alert_test: template: __alert_test:1:86: executing \"__alert_test\" at <$values.A.Labels.instance>: map has no entry for key \"instance\""),
|
||||
}, {
|
||||
name: "value string is expanded into $value",
|
||||
text: "{{ $value }}",
|
||||
alertInstance: eval.Result{
|
||||
EvaluationString: "[ var='A' labels={instance=foo} value=10 ]",
|
||||
},
|
||||
expected: "[ var='A' labels={instance=foo} value=10 ]",
|
||||
}}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
v, err := expandTemplate("test", c.text, c.labels, c.alertInstance)
|
||||
require.Equal(t, c.expectedError, err)
|
||||
require.Equal(t, c.expected, v)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user