Support customization of template options in TemplateExpander (#9290)

Signed-off-by: George Robinson <george.robinson@grafana.com>
This commit is contained in:
George Robinson 2021-09-13 12:49:08 +01:00 committed by GitHub
parent 46286cb6ab
commit 049b4f4f13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 4 deletions

View file

@ -239,6 +239,7 @@ func testTemplateParsing(rl *RuleNode) (errs []error) {
model.Time(timestamp.FromTime(time.Now())), model.Time(timestamp.FromTime(time.Now())),
nil, nil,
nil, nil,
nil,
) )
return tmpl.ParseTest() return tmpl.ParseTest()
} }

View file

@ -338,6 +338,7 @@ func (r *AlertingRule) Eval(ctx context.Context, ts time.Time, query QueryFunc,
model.Time(timestamp.FromTime(ts)), model.Time(timestamp.FromTime(ts)),
template.QueryFunc(query), template.QueryFunc(query),
externalURL, externalURL,
nil,
) )
result, err := tmpl.Expand() result, err := tmpl.Expand()
if err != nil { if err != nil {

View file

@ -115,6 +115,7 @@ type Expander struct {
name string name string
data interface{} data interface{}
funcMap text_template.FuncMap funcMap text_template.FuncMap
options []string
} }
// NewTemplateExpander returns a template expander ready to use. // NewTemplateExpander returns a template expander ready to use.
@ -126,7 +127,11 @@ func NewTemplateExpander(
timestamp model.Time, timestamp model.Time,
queryFunc QueryFunc, queryFunc QueryFunc,
externalURL *url.URL, externalURL *url.URL,
options []string,
) *Expander { ) *Expander {
if options == nil {
options = []string{"missingkey=zero"}
}
return &Expander{ return &Expander{
text: text, text: text,
name: name, name: name,
@ -291,6 +296,7 @@ func NewTemplateExpander(
return externalURL.String() return externalURL.String()
}, },
}, },
options: options,
} }
} }
@ -336,7 +342,9 @@ func (te Expander) Expand() (result string, resultErr error) {
templateTextExpansionTotal.Inc() templateTextExpansionTotal.Inc()
tmpl, err := text_template.New(te.name).Funcs(te.funcMap).Option("missingkey=zero").Parse(te.text) tmpl := text_template.New(te.name).Funcs(te.funcMap)
tmpl.Option(te.options...)
tmpl, err := tmpl.Parse(te.text)
if err != nil { if err != nil {
return "", errors.Wrapf(err, "error parsing template %v", te.name) return "", errors.Wrapf(err, "error parsing template %v", te.name)
} }
@ -361,7 +369,7 @@ func (te Expander) ExpandHTML(templateFiles []string) (result string, resultErr
}() }()
tmpl := html_template.New(te.name).Funcs(html_template.FuncMap(te.funcMap)) tmpl := html_template.New(te.name).Funcs(html_template.FuncMap(te.funcMap))
tmpl.Option("missingkey=zero") tmpl.Option(te.options...)
tmpl.Funcs(html_template.FuncMap{ tmpl.Funcs(html_template.FuncMap{
"tmpl": func(name string, data interface{}) (html_template.HTML, error) { "tmpl": func(name string, data interface{}) (html_template.HTML, error) {
var buffer bytes.Buffer var buffer bytes.Buffer

View file

@ -31,6 +31,7 @@ func TestTemplateExpansion(t *testing.T) {
text string text string
output string output string
input interface{} input interface{}
options []string
queryResult promql.Vector queryResult promql.Vector
shouldFail bool shouldFail bool
html bool html bool
@ -153,6 +154,45 @@ func TestTemplateExpansion(t *testing.T) {
}}, }},
output: "a:11: b:21: ", output: "a:11: b:21: ",
}, },
{
// Missing value is no value for nil options.
text: "{{ .Foo }}",
output: "<no value>",
},
{
// Missing value is no value for no options.
text: "{{ .Foo }}",
options: make([]string, 0),
output: "<no value>",
},
{
// Assert that missing value returns error with missingkey=error.
text: "{{ .Foo }}",
options: []string{"missingkey=error"},
shouldFail: true,
errorMsg: `error executing template test: template: test:1:3: executing "test" at <.Foo>: nil data; no entry for key "Foo"`,
},
{
// Missing value is "" for nil options in ExpandHTML.
text: "{{ .Foo }}",
output: "",
html: true,
},
{
// Missing value is "" for no options in ExpandHTML.
text: "{{ .Foo }}",
options: make([]string, 0),
output: "",
html: true,
},
{
// Assert that missing value returns error with missingkey=error in ExpandHTML.
text: "{{ .Foo }}",
options: []string{"missingkey=error"},
shouldFail: true,
errorMsg: `error executing template test: template: test:1:3: executing "test" at <.Foo>: nil data; no entry for key "Foo"`,
html: true,
},
{ {
// Unparsable template. // Unparsable template.
text: "{{", text: "{{",
@ -341,7 +381,7 @@ func TestTemplateExpansion(t *testing.T) {
} }
var result string var result string
var err error var err error
expander := NewTemplateExpander(context.Background(), s.text, "test", s.input, 0, queryFunc, extURL) expander := NewTemplateExpander(context.Background(), s.text, "test", s.input, 0, queryFunc, extURL, s.options)
if s.html { if s.html {
result, err = expander.ExpandHTML(nil) result, err = expander.ExpandHTML(nil)
} else { } else {
@ -356,7 +396,7 @@ func TestTemplateExpansion(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
if err == nil { if err == nil {
require.Equal(t, result, s.output) require.Equal(t, s.output, result)
} }
} }
} }

View file

@ -709,6 +709,7 @@ func (h *Handler) consoles(w http.ResponseWriter, r *http.Request) {
h.now(), h.now(),
template.QueryFunc(rules.EngineQueryFunc(h.queryEngine, h.storage)), template.QueryFunc(rules.EngineQueryFunc(h.queryEngine, h.storage)),
h.options.ExternalURL, h.options.ExternalURL,
nil,
) )
filenames, err := filepath.Glob(h.options.ConsoleLibrariesPath + "/*.lib") filenames, err := filepath.Glob(h.options.ConsoleLibrariesPath + "/*.lib")
if err != nil { if err != nil {
@ -1113,6 +1114,7 @@ func (h *Handler) executeTemplate(w http.ResponseWriter, name string, data inter
h.now(), h.now(),
template.QueryFunc(rules.EngineQueryFunc(h.queryEngine, h.storage)), template.QueryFunc(rules.EngineQueryFunc(h.queryEngine, h.storage)),
h.options.ExternalURL, h.options.ExternalURL,
nil,
) )
tmpl.Funcs(tmplFuncs(h.consolesPath(), h.options)) tmpl.Funcs(tmplFuncs(h.consolesPath(), h.options))