diff --git a/docs/custom-annotations.md b/docs/custom-annotations.md index 850050765e..2e40fb65a4 100644 --- a/docs/custom-annotations.md +++ b/docs/custom-annotations.md @@ -86,6 +86,37 @@ If you'd like to use custom annotations with Mergeable Ingress resources, please * Minions do not inherent custom annotations of the master. +### Helper Functions + +Helper functions can be used in the Ingress template to parse the values of custom annotations. + +| Function | Input Arguments| Return Arguments | Description | +| -------- | -------------- | ---------------- | ----------- | +| `split` | `s, sep string` | `[]string` | Splits the string `s` into a slice of strings separated by the `sep`. | +| `trim` | `s string` | `string` | Trims the trailing and leading whitespace from the string `s`. | + +Consider the following custom annotation `custom.nginx.org/allowed-ips`, which expects a comma-separated list of IP addresses: +``` +annotations: + custom.nginx.org/allowed-ips: "192.168.1.3, 10.0.0.13" +``` + + The helper functions can parse the value of the `custom.nginx.org/allowed-ips` annotation, so that in the template you can use each IP address separately. Consider the following template excerpt: + +``` +{{range $ip := split (index $.Ingress.Annotations "custom.nginx.org/allowed-ips") ","}} + allow {{trim $ip}}; +{{end}} +deny all; +``` + +The template excerpt will generate the following configuration: +``` +allow 192.168.1.3; +allow 10.0.0.13; +deny all; +``` + ## Example See the [custom annotations example](../examples/custom-annotations). \ No newline at end of file diff --git a/internal/configs/version1/template_executor.go b/internal/configs/version1/template_executor.go index b03b30ad90..d2eaae8304 100644 --- a/internal/configs/version1/template_executor.go +++ b/internal/configs/version1/template_executor.go @@ -20,7 +20,7 @@ func NewTemplateExecutor(mainTemplatePath string, ingressTemplatePath string) (* return nil, err } - ingressTemplate, err := template.New(path.Base(ingressTemplatePath)).ParseFiles(ingressTemplatePath) + ingressTemplate, err := template.New(path.Base(ingressTemplatePath)).Funcs(helperFunctions).ParseFiles(ingressTemplatePath) if err != nil { return nil, err } @@ -45,7 +45,7 @@ func (te *TemplateExecutor) UpdateMainTemplate(templateString *string) error { // UpdateIngressTemplate updates the ingress template. func (te *TemplateExecutor) UpdateIngressTemplate(templateString *string) error { - newTemplate, err := template.New("ingressTemplate").Parse(*templateString) + newTemplate, err := template.New("ingressTemplate").Funcs(helperFunctions).Parse(*templateString) if err != nil { return err } diff --git a/internal/configs/version1/template_helper.go b/internal/configs/version1/template_helper.go new file mode 100644 index 0000000000..c2b09f5c61 --- /dev/null +++ b/internal/configs/version1/template_helper.go @@ -0,0 +1,19 @@ +package version1 + +import ( + "strings" + "text/template" +) + +func split(s string, delim string) []string { + return strings.Split(s, delim) +} + +func trim(s string) string { + return strings.TrimSpace(s) +} + +var helperFunctions = template.FuncMap{ + "split": split, + "trim": trim, +} diff --git a/internal/configs/version1/templates_test.go b/internal/configs/version1/templates_test.go index ddb894e88f..8a863dcc7c 100644 --- a/internal/configs/version1/templates_test.go +++ b/internal/configs/version1/templates_test.go @@ -111,7 +111,7 @@ var mainCfg = MainConfig{ } func TestIngressForNGINXPlus(t *testing.T) { - tmpl, err := template.New(nginxPlusIngressTmpl).ParseFiles(nginxPlusIngressTmpl) + tmpl, err := template.New(nginxPlusIngressTmpl).Funcs(helperFunctions).ParseFiles(nginxPlusIngressTmpl) if err != nil { t.Fatalf("Failed to parse template file: %v", err) } @@ -126,7 +126,7 @@ func TestIngressForNGINXPlus(t *testing.T) { } func TestIngressForNGINX(t *testing.T) { - tmpl, err := template.New(nginxIngressTmpl).ParseFiles(nginxIngressTmpl) + tmpl, err := template.New(nginxIngressTmpl).Funcs(helperFunctions).ParseFiles(nginxIngressTmpl) if err != nil { t.Fatalf("Failed to parse template file: %v", err) } @@ -169,3 +169,49 @@ func TestMainForNGINX(t *testing.T) { t.Fatalf("Failed to write template %v", err) } } + +func TestSplitHelperFunction(t *testing.T) { + const tpl = `{{range $n := split . ","}}{{$n}} {{end}}` + + tmpl, err := template.New("testTemplate").Funcs(helperFunctions).Parse(tpl) + if err != nil { + t.Fatalf("Failed to parse template: %v", err) + } + + var buf bytes.Buffer + + input := "foo,bar" + expected := "foo bar " + + err = tmpl.Execute(&buf, input) + if err != nil { + t.Fatalf("Failed to execute the template %v", err) + } + + if buf.String() != expected { + t.Fatalf("Template generated wrong config, got %v but expected %v.", buf.String(), expected) + } +} + +func TestTrimHelperFunction(t *testing.T) { + const tpl = `{{trim .}}` + + tmpl, err := template.New("testTemplate").Funcs(helperFunctions).Parse(tpl) + if err != nil { + t.Fatalf("Failed to parse template: %v", err) + } + + var buf bytes.Buffer + + input := " foobar " + expected := "foobar" + + err = tmpl.Execute(&buf, input) + if err != nil { + t.Fatalf("Failed to execute the template %v", err) + } + + if buf.String() != expected { + t.Fatalf("Template generated wrong config, got %v but expected %v.", buf.String(), expected) + } +}