From 4c0dba405478cd66145b094441154ce0be3e9f2f Mon Sep 17 00:00:00 2001 From: ampant Date: Wed, 11 Sep 2019 16:26:40 +0530 Subject: [PATCH 1/3] Add helper funtions for custom annotations --- docs/custom-annotations.md | 31 ++++++++++++ .../configs/version1/template_executor.go | 4 +- internal/configs/version1/template_helper.go | 22 +++++++++ internal/configs/version1/templates_test.go | 48 ++++++++++++++++++- 4 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 internal/configs/version1/template_helper.go diff --git a/docs/custom-annotations.md b/docs/custom-annotations.md index 850050765e..4b5f6f33b1 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 custom templates to transform the value of custom annotations. + +| Function | Description | +| -------- | ----------- | +| `splitinput` | Splits the arguments at specified delimiter and returns an array of strings | +| `triminput` | Trims the trailing and leading whitespaces from the arguments | + +For example, the following custom annotation custom.nginx.org/allowed-ips represents a comma separated list of IP addresses as a string. +``` +annotations: + custom.nginx.org/allowed-ips: "192.168.1.3, 10.0.0.13" +``` + + It is possible to use helper functions such as splitinput and triminput to transform the annotation into something meaningful for NGINX. The example below shows how to use these functions in a custom template for the given annotation: + +``` +{{range $ip := splitinput (index $.Ingress.Annotations "custom.nginx.org/allowed-ips") ","}} + allow {{triminput $ip}}; +{{end}} +deny all; +``` + +The template 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..635412fd86 --- /dev/null +++ b/internal/configs/version1/template_helper.go @@ -0,0 +1,22 @@ +package version1 + +import ( + "strings" + "text/template" +) + +// SplitInput splits the input from "," and returns an array of strings +func SplitInput(s string, delim string) []string { + return strings.Split(s, delim) +} + +// TrimInput trims the leading and trailing spaces in the string +func TrimInput(s string) string { + return strings.TrimSpace(s) +} + +// HelperFunctions to parse the annotations +var helperFunctions = template.FuncMap{ + "splitinput": SplitInput, //returns array of strings + "triminput": TrimInput, //returns string with trimmed leading and trailing spaces +} diff --git a/internal/configs/version1/templates_test.go b/internal/configs/version1/templates_test.go index ddb894e88f..132d2fd7e9 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,47 @@ func TestMainForNGINX(t *testing.T) { t.Fatalf("Failed to write template %v", err) } } + +func TestSplitHelperFunctions(t *testing.T) { + const tpl = `{{range $n := splitinput (index .) ","}}{{$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 + + slice := "foo,bar" + expected := "foo bar " + err = tmpl.Execute(&buf, slice) + t.Log(buf.String()) + if err != nil { + t.Fatalf("Failed to write template %v", err) + } + + if buf.String() != expected { + t.Fatalf("Failed parsing the template, got %v but expected %v.", buf.String(), expected) + } +} + +func TestTrimHelperFunctions(t *testing.T) { + const tpl = `{{triminput (index .)}}` + + tmpl, err := template.New("testTemplate").Funcs(helperFunctions).Parse(tpl) + if err != nil { + t.Fatalf("Failed to parse template: %v", err) + } + var buf bytes.Buffer + + slice := " foobar " + expected := "foobar" + err = tmpl.Execute(&buf, slice) + t.Log(buf.String()) + if err != nil { + t.Fatalf("Failed to write template %v", err) + } + + if buf.String() != expected { + t.Fatalf("Failed parsing the template, got %v but expected %v.", buf.String(), expected) + } +} From f48dcdf64b87decf55de52c9a2de533e8f358294 Mon Sep 17 00:00:00 2001 From: ampant Date: Fri, 13 Sep 2019 12:00:43 +0530 Subject: [PATCH 2/3] Addressed the comments --- docs/custom-annotations.md | 20 +++++++------- internal/configs/version1/template_helper.go | 13 +++++---- internal/configs/version1/templates_test.go | 28 +++++++++++--------- 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/docs/custom-annotations.md b/docs/custom-annotations.md index 4b5f6f33b1..f23523561f 100644 --- a/docs/custom-annotations.md +++ b/docs/custom-annotations.md @@ -88,29 +88,29 @@ If you'd like to use custom annotations with Mergeable Ingress resources, please ### Helper Functions -Helper functions can be used in custom templates to transform the value of custom annotations. +Helper functions can be used in the Ingress template to parse the values of custom annotations. -| Function | Description | -| -------- | ----------- | -| `splitinput` | Splits the arguments at specified delimiter and returns an array of strings | -| `triminput` | Trims the trailing and leading whitespaces from the arguments | +| 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. | -For example, the following custom annotation custom.nginx.org/allowed-ips represents a comma separated list of IP addresses as a string. +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" ``` - It is possible to use helper functions such as splitinput and triminput to transform the annotation into something meaningful for NGINX. The example below shows how to use these functions in a custom template for the given annotation: + 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 := splitinput (index $.Ingress.Annotations "custom.nginx.org/allowed-ips") ","}} - allow {{triminput $ip}}; +{{range $ip := split (index $.Ingress.Annotations "custom.nginx.org/allowed-ips") ","}} + allow {{trim $ip}}; {{end}} deny all; ``` -The template will generate the following configuration: +The template excerpt will generate the following configuration: ``` allow 192.168.1.3; allow 10.0.0.13; diff --git a/internal/configs/version1/template_helper.go b/internal/configs/version1/template_helper.go index 635412fd86..cce493e2bb 100644 --- a/internal/configs/version1/template_helper.go +++ b/internal/configs/version1/template_helper.go @@ -5,18 +5,17 @@ import ( "text/template" ) -// SplitInput splits the input from "," and returns an array of strings -func SplitInput(s string, delim string) []string { +// splitinput splits the input from "," and returns an array of strings +func splitinput(s string, delim string) []string { return strings.Split(s, delim) } -// TrimInput trims the leading and trailing spaces in the string -func TrimInput(s string) string { +// triminput trims the leading and trailing spaces in the string +func triminput(s string) string { return strings.TrimSpace(s) } -// HelperFunctions to parse the annotations var helperFunctions = template.FuncMap{ - "splitinput": SplitInput, //returns array of strings - "triminput": TrimInput, //returns string with trimmed leading and trailing spaces + "split": splitinput, //returns array of strings + "trim": triminput, //returns string with trimmed leading and trailing spaces } diff --git a/internal/configs/version1/templates_test.go b/internal/configs/version1/templates_test.go index 132d2fd7e9..62340ab6d5 100644 --- a/internal/configs/version1/templates_test.go +++ b/internal/configs/version1/templates_test.go @@ -170,46 +170,50 @@ func TestMainForNGINX(t *testing.T) { } } -func TestSplitHelperFunctions(t *testing.T) { - const tpl = `{{range $n := splitinput (index .) ","}}{{$n}} {{end}}` +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 - slice := "foo,bar" + input := "foo,bar" expected := "foo bar " - err = tmpl.Execute(&buf, slice) + + err = tmpl.Execute(&buf, input) t.Log(buf.String()) if err != nil { - t.Fatalf("Failed to write template %v", err) + t.Fatalf("Failed to execute the template %v", err) } if buf.String() != expected { - t.Fatalf("Failed parsing the template, got %v but expected %v.", buf.String(), expected) + t.Fatalf("Template generated wrong config, got %v but expected %v.", buf.String(), expected) } } -func TestTrimHelperFunctions(t *testing.T) { - const tpl = `{{triminput (index .)}}` +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 - slice := " foobar " + input := " foobar " expected := "foobar" - err = tmpl.Execute(&buf, slice) + + err = tmpl.Execute(&buf, input) t.Log(buf.String()) if err != nil { - t.Fatalf("Failed to write template %v", err) + t.Fatalf("Failed to execute the template %v", err) } if buf.String() != expected { - t.Fatalf("Failed parsing the template, got %v but expected %v.", buf.String(), expected) + t.Fatalf("Template generated wrong config, got %v but expected %v.", buf.String(), expected) } } From a1f8163f0c858283ca4c309e26497bcdcd1ee9fc Mon Sep 17 00:00:00 2001 From: ampant Date: Mon, 16 Sep 2019 14:50:24 +0530 Subject: [PATCH 3/3] Addressed changes --- docs/custom-annotations.md | 4 ++-- internal/configs/version1/template_helper.go | 10 ++++------ internal/configs/version1/templates_test.go | 2 -- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/custom-annotations.md b/docs/custom-annotations.md index f23523561f..2e40fb65a4 100644 --- a/docs/custom-annotations.md +++ b/docs/custom-annotations.md @@ -92,8 +92,8 @@ Helper functions can be used in the Ingress template to parse the values of cust | 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. | +| `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: ``` diff --git a/internal/configs/version1/template_helper.go b/internal/configs/version1/template_helper.go index cce493e2bb..c2b09f5c61 100644 --- a/internal/configs/version1/template_helper.go +++ b/internal/configs/version1/template_helper.go @@ -5,17 +5,15 @@ import ( "text/template" ) -// splitinput splits the input from "," and returns an array of strings -func splitinput(s string, delim string) []string { +func split(s string, delim string) []string { return strings.Split(s, delim) } -// triminput trims the leading and trailing spaces in the string -func triminput(s string) string { +func trim(s string) string { return strings.TrimSpace(s) } var helperFunctions = template.FuncMap{ - "split": splitinput, //returns array of strings - "trim": triminput, //returns string with trimmed leading and trailing spaces + "split": split, + "trim": trim, } diff --git a/internal/configs/version1/templates_test.go b/internal/configs/version1/templates_test.go index 62340ab6d5..8a863dcc7c 100644 --- a/internal/configs/version1/templates_test.go +++ b/internal/configs/version1/templates_test.go @@ -184,7 +184,6 @@ func TestSplitHelperFunction(t *testing.T) { expected := "foo bar " err = tmpl.Execute(&buf, input) - t.Log(buf.String()) if err != nil { t.Fatalf("Failed to execute the template %v", err) } @@ -208,7 +207,6 @@ func TestTrimHelperFunction(t *testing.T) { expected := "foobar" err = tmpl.Execute(&buf, input) - t.Log(buf.String()) if err != nil { t.Fatalf("Failed to execute the template %v", err) }