diff --git a/docs/sources/alerting/unified-alerting/alerting-rules/alert-annotation-label.md b/docs/sources/alerting/unified-alerting/alerting-rules/alert-annotation-label.md index f85016c4bff..eaf26a965d5 100644 --- a/docs/sources/alerting/unified-alerting/alerting-rules/alert-annotation-label.md +++ b/docs/sources/alerting/unified-alerting/alerting-rules/alert-annotation-label.md @@ -33,28 +33,28 @@ The following template variables are available when expanding annotations and la The following template functions are available when expanding annotations and labels. -| Name | Argument | Return | Description | -| ------------------ | -------------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| humanize | number or string | string | Converts a number to a more readable format, using metric prefixes. | -| humanize1024 | number or string | string | Like humanize, but uses 1024 as the base rather than 1000. | -| humanizeDuration | number or string | string | Converts a duration in seconds to a more readable format. | -| humanizePercentage | number or string | string | Converts a ratio value to a fraction of 100. | -| humanizeTimestamp | number or string | string | Converts a Unix timestamp in seconds to a more readable format. | -| title | string | string | strings.Title, capitalises first character of each word. | -| toUpper | string | string | strings.ToUpper, converts all characters to upper case. | -| toLower | string | string | strings.ToLower, converts all characters to lower case. | -| match | pattern, text | boolean | regexp.MatchString Tests for a unanchored regexp match. | -| reReplaceAll | pattern, replacement, text | string | Regexp.ReplaceAllString Regexp substitution, unanchored. | -| graphLink | expr | string | Not supported | -| tableLink | expr | string | Not supported | -| args | []interface{} | map[string]interface{} | Converts a list of objects to a map with keys, for example, arg0, arg1. Use this function to pass multiple arguments to templates. | -| externalURL | nothing | string | Returns a string representing the external URL. | -| pathPrefix | nothing | string | Returns the path of the external URL. | -| tmpl | string, []interface{} | nothing | Not supported | -| safeHtml | string | string | Not supported | -| query | query string | []sample | Not supported | -| first | []sample | sample | Not supported | -| label | label, sample | string | Not supported | -| strvalue | []sample | string | Not supported | -| value | sample | float64 | Not supported | -| sortByLabel | label, []samples | []sample | Not supported | +| Name | Argument | Return | Description | +| ------------------ | ------------------------------------------------------------ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | +| humanize | number or string | string | Converts a number to a more readable format, using metric prefixes. | +| humanize1024 | number or string | string | Like humanize, but uses 1024 as the base rather than 1000. | +| humanizeDuration | number or string | string | Converts a duration in seconds to a more readable format. | +| humanizePercentage | number or string | string | Converts a ratio value to a fraction of 100. | +| humanizeTimestamp | number or string | string | Converts a Unix timestamp in seconds to a more readable format. | +| title | string | string | strings.Title, capitalises first character of each word. | +| toUpper | string | string | strings.ToUpper, converts all characters to upper case. | +| toLower | string | string | strings.ToLower, converts all characters to lower case. | +| match | pattern, text | boolean | regexp.MatchString Tests for a unanchored regexp match. | +| reReplaceAll | pattern, replacement, text | string | Regexp.ReplaceAllString Regexp substitution, unanchored. | +| graphLink | string - JSON Object with `"expr"` and `"datasource"` fields | string | Returns the path to graphical view in [Explore](https://grafana.com/docs/grafana/latest/explore/) for the given expression and data source. | +| tableLink | string- JSON Object with `"expr"` and `"datasource"` fields | string | Returns the path to tabular view in [Explore](https://grafana.com/docs/grafana/latest/explore/) for the given expression and data source. | +| args | []interface{} | map[string]interface{} | Converts a list of objects to a map with keys, for example, arg0, arg1. Use this function to pass multiple arguments to templates. | +| externalURL | nothing | string | Returns a string representing the external URL. | +| pathPrefix | nothing | string | Returns the path of the external URL. | +| tmpl | string, []interface{} | nothing | Not supported | +| safeHtml | string | string | Not supported | +| query | query string | []sample | Not supported | +| first | []sample | sample | Not supported | +| label | label, sample | string | Not supported | +| strvalue | []sample | string | Not supported | +| value | sample | float64 | Not supported | +| sortByLabel | label, []samples | []sample | Not supported | diff --git a/pkg/services/ngalert/state/template.go b/pkg/services/ngalert/state/template.go index 81d66760f9e..301c31ae525 100644 --- a/pkg/services/ngalert/state/template.go +++ b/pkg/services/ngalert/state/template.go @@ -2,6 +2,8 @@ package state import ( "context" + "encoding/json" + "fmt" "math" "net/url" "strconv" @@ -56,16 +58,13 @@ func expandTemplate(name, text string, labels map[string]string, alertInstance e ) expander.Funcs(text_template.FuncMap{ - // These three functions are no-ops for now. + "graphLink": graphLink, + "tableLink": tableLink, + + // This function is a no-op for now. "strvalue": func(value templateCaptureValue) string { return "" }, - "graphLink": func() string { - return "" - }, - "tableLink": func() string { - return "" - }, }) return expander.Expand() @@ -87,3 +86,34 @@ func newTemplateCaptureValues(values map[string]eval.NumberValueCapture) map[str } return m } + +type query struct { + Datasource string `json:"datasource"` + Expr string `json:"expr"` +} + +func graphLink(rawQuery string) string { + var q query + if err := json.Unmarshal([]byte(rawQuery), &q); err != nil { + return "" + } + + escapedExpression := url.QueryEscape(q.Expr) + escapedDatasource := url.QueryEscape(q.Datasource) + + return fmt.Sprintf( + `/explore?left=["now-1h","now",%[1]q,{"datasource":%[1]q,"expr":%q,"instant":false,"range":true}]`, escapedDatasource, escapedExpression) +} + +func tableLink(rawQuery string) string { + var q query + if err := json.Unmarshal([]byte(rawQuery), &q); err != nil { + return "" + } + + escapedExpression := url.QueryEscape(q.Expr) + escapedDatasource := url.QueryEscape(q.Datasource) + + return fmt.Sprintf( + `/explore?left=["now-1h","now",%[1]q,{"datasource":%[1]q,"expr":%q,"instant":true,"range":false}]`, escapedDatasource, escapedExpression) +} diff --git a/pkg/services/ngalert/state/template_test.go b/pkg/services/ngalert/state/template_test.go index 9544ec4c892..1eef5f7230f 100644 --- a/pkg/services/ngalert/state/template_test.go +++ b/pkg/services/ngalert/state/template_test.go @@ -355,11 +355,27 @@ func TestExpandTemplate(t *testing.T) { text: "{{ query \"metric{instance='a'}\" | first | label \"instance\" }}", expected: "", }, { - name: "check that graphLink returns an empty string", + name: "graphLink", + text: `{{ graphLink "{\"expr\": \"up\", \"datasource\": \"gdev-prometheus\"}" }}`, + expected: `/explore?left=["now-1h","now","gdev-prometheus",{"datasource":"gdev-prometheus","expr":"up","instant":false,"range":true}]`, + }, { + name: "graphLink should escape both the expression and the datasource", + text: `{{ graphLink "{\"expr\": \"process_open_fds > 0\", \"datasource\": \"gdev prometheus\"}" }}`, + expected: `/explore?left=["now-1h","now","gdev+prometheus",{"datasource":"gdev+prometheus","expr":"process_open_fds+%3E+0","instant":false,"range":true}]`, + }, { + name: "check that graphLink returns an empty string when the query is not formatted correctly", text: "{{ graphLink \"up\" }}", expected: "", }, { - name: "check that tableLink returns an empty string", + name: "tableLink", + text: `{{ tableLink "{\"expr\": \"up\", \"datasource\": \"gdev-prometheus\"}" }}`, + expected: `/explore?left=["now-1h","now","gdev-prometheus",{"datasource":"gdev-prometheus","expr":"up","instant":true,"range":false}]`, + }, { + name: "tableLink should escape both the expression and the datasource", + text: `{{ tableLink "{\"expr\": \"process_open_fds > 0\", \"datasource\": \"gdev prometheus\"}" }}`, + expected: `/explore?left=["now-1h","now","gdev+prometheus",{"datasource":"gdev+prometheus","expr":"process_open_fds+%3E+0","instant":true,"range":false}]`, + }, { + name: "check that tableLink returns an empty string when the query is not formatted correctly", text: "{{ tableLink \"up\" }}", expected: "", }, {