Alerting: Evaluate data templating in alert rule name and message (#29908)

* evaluate Go style template

* inlince func

* add test case

* PR feedback and add tests for templte data map func

* Add test case

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* do regex check

* ensure ecape

* small cleanup

* dont exit on template execution errors

* add info tooltip

* add docs

* switch from go tmpl to regex

* update docs/comments

* update tooltip wording

* update docs wording

* add simple test

* avoid .MustCompile

* point to labels in docs

* update docs

* fix docs links

* remove line

* fix lint

* add note about multiple labels

* propagate labels for CM

* update docs

* remove whitespace

* update task title

* update docs

* pr feedback

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
This commit is contained in:
Will Browne
2021-01-19 22:02:44 +01:00
committed by GitHub
parent 446db193eb
commit c9da053e5d
9 changed files with 282 additions and 19 deletions

View File

@@ -3,6 +3,7 @@ package alerting
import (
"context"
"fmt"
"regexp"
"time"
"github.com/grafana/grafana/pkg/bus"
@@ -178,3 +179,66 @@ func getNewStateInternal(c *EvalContext) models.AlertStateType {
return models.AlertStateOK
}
// evaluateNotificationTemplateFields will treat the alert evaluation rule's name and message fields as
// templates, and evaluate the templates using data from the alert evaluation's tags
func (c *EvalContext) evaluateNotificationTemplateFields() error {
if len(c.EvalMatches) < 1 {
return nil
}
templateDataMap, err := buildTemplateDataMap(c.EvalMatches)
if err != nil {
return err
}
ruleMsg, err := evaluateTemplate(c.Rule.Message, templateDataMap)
if err != nil {
return err
}
c.Rule.Message = ruleMsg
ruleName, err := evaluateTemplate(c.Rule.Name, templateDataMap)
if err != nil {
return err
}
c.Rule.Name = ruleName
return nil
}
func evaluateTemplate(s string, m map[string]string) (string, error) {
for k, v := range m {
re, err := regexp.Compile(fmt.Sprintf(`\${%s}`, regexp.QuoteMeta(k)))
if err != nil {
return "", err
}
s = re.ReplaceAllString(s, v)
}
return s, nil
}
// buildTemplateDataMap builds a map of alert evaluation tag names to a set of associated values (comma separated)
func buildTemplateDataMap(evalMatches []*EvalMatch) (map[string]string, error) {
var result = map[string]string{}
for _, match := range evalMatches {
for tagName, tagValue := range match.Tags {
// skip duplicate values
rVal, err := regexp.Compile(fmt.Sprintf(`\b%s\b`, regexp.QuoteMeta(tagValue)))
if err != nil {
return nil, err
}
rMatch := rVal.FindString(result[tagName])
if len(rMatch) > 0 {
continue
}
if _, exists := result[tagName]; exists {
result[tagName] = fmt.Sprintf("%s, %s", result[tagName], tagValue)
} else {
result[tagName] = tagValue
}
}
}
return result, nil
}