Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add string comparison functions to Go template executors #4560

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,15 @@ If you'd like to use custom annotations with Mergeable Ingress resources, please
Helper functions can be used in the Ingress template to parse the values of custom annotations.

{{% table %}}
|Function | Input Arguments | Return Arguments | Description |
| 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``. |
| ``contains`` | ``s, substr string`` | ``bool`` | Tests whether the string ``substr`` is a substring of the string ``s``. |
| ``hasPrefix`` | ``s, prefix string`` | ``bool`` | Tests whether the string ``prefix`` is a prefix of the string ``s``. |
| ``hasSuffix`` | ``s, suffix string`` | ``bool`` | Tests whether the string ``suffix`` is a suffix of the string ``s``. |
| ``toLower`` | ``s string`` | ``bool`` | Converts all letters in the string ``s`` to their lower case. |
| ``toUpper`` | ``s string`` | ``bool`` | Converts all letters in the string ``s`` to their upper case. |
{{% /table %}}

Consider the following custom annotation `custom.nginx.org/allowed-ips`, which expects a comma-separated list of IP addresses:
Expand Down
5 changes: 5 additions & 0 deletions internal/configs/version1/template_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,10 @@ func makePathWithRegex(path, regexType string) string {
var helperFunctions = template.FuncMap{
"split": split,
"trim": trim,
"contains": strings.Contains,
"hasPrefix": strings.HasPrefix,
"hasSuffix": strings.HasSuffix,
"toLower": strings.ToLower,
"toUpper": strings.ToUpper,
"makeLocationPath": makeLocationPath,
}
184 changes: 184 additions & 0 deletions internal/configs/version1/template_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,145 @@ func TestTrimWhiteSpaceFromInputString(t *testing.T) {
}
}

func TestContainsSubstring(t *testing.T) {
t.Parallel()

tmpl := newContainsTemplate(t)
testCases := []struct {
InputString string
Substring string
expected string
}{
{InputString: "foo", Substring: "foo", expected: "true"},
{InputString: "foobar", Substring: "foo", expected: "true"},
{InputString: "foo", Substring: "", expected: "true"},
{InputString: "foo", Substring: "bar", expected: "false"},
{InputString: "foo", Substring: "foobar", expected: "false"},
{InputString: "", Substring: "foo", expected: "false"},
}

for _, tc := range testCases {
var buf bytes.Buffer
err := tmpl.Execute(&buf, tc)
if err != nil {
t.Fatalf("Failed to execute the template %v", err)
}
if buf.String() != tc.expected {
t.Errorf("Template generated wrong config, got %v but expected %v.", buf.String(), tc.expected)
}
}
}

func TestHasPrefix(t *testing.T) {
t.Parallel()

tmpl := newHasPrefixTemplate(t)
testCases := []struct {
InputString string
Prefix string
expected string
}{
{InputString: "foo", Prefix: "foo", expected: "true"},
{InputString: "foo", Prefix: "f", expected: "true"},
{InputString: "foo", Prefix: "", expected: "true"},
{InputString: "foo", Prefix: "oo", expected: "false"},
{InputString: "foo", Prefix: "bar", expected: "false"},
{InputString: "foo", Prefix: "foobar", expected: "false"},
}

for _, tc := range testCases {
var buf bytes.Buffer
err := tmpl.Execute(&buf, tc)
if err != nil {
t.Fatalf("Failed to execute the template %v", err)
}
if buf.String() != tc.expected {
t.Errorf("Template generated wrong config, got %v but expected %v.", buf.String(), tc.expected)
}
}
}

func TestHasSuffix(t *testing.T) {
t.Parallel()

tmpl := newHasSuffixTemplate(t)
testCases := []struct {
InputString string
Suffix string
expected string
}{
{InputString: "bar", Suffix: "bar", expected: "true"},
{InputString: "bar", Suffix: "r", expected: "true"},
{InputString: "bar", Suffix: "", expected: "true"},
{InputString: "bar", Suffix: "ba", expected: "false"},
{InputString: "bar", Suffix: "foo", expected: "false"},
{InputString: "bar", Suffix: "foobar", expected: "false"},
}

for _, tc := range testCases {
var buf bytes.Buffer
err := tmpl.Execute(&buf, tc)
if err != nil {
t.Fatalf("Failed to execute the template %v", err)
}
if buf.String() != tc.expected {
t.Errorf("Template generated wrong config, got %v but expected %v.", buf.String(), tc.expected)
}
}
}

func TestToLowerInputString(t *testing.T) {
t.Parallel()

tmpl := newToLowerTemplate(t)
testCases := []struct {
InputString string
expected string
}{
{InputString: "foobar", expected: "foobar"},
{InputString: "FOOBAR", expected: "foobar"},
{InputString: "fOoBaR", expected: "foobar"},
{InputString: "", expected: ""},
}

for _, tc := range testCases {
var buf bytes.Buffer
err := tmpl.Execute(&buf, tc)
if err != nil {
t.Fatalf("Failed to execute the template %v", err)
}
if buf.String() != tc.expected {
t.Errorf("Template generated wrong config, got %v but expected %v.", buf.String(), tc.expected)
}
}
}

func TestToUpperInputString(t *testing.T) {
t.Parallel()

tmpl := newToUpperTemplate(t)
testCases := []struct {
InputString string
expected string
}{
{InputString: "foobar", expected: "FOOBAR"},
{InputString: "FOOBAR", expected: "FOOBAR"},
{InputString: "fOoBaR", expected: "FOOBAR"},
{InputString: "", expected: ""},
}

for _, tc := range testCases {
var buf bytes.Buffer
err := tmpl.Execute(&buf, tc)
if err != nil {
t.Fatalf("Failed to execute the template %v", err)
}
if buf.String() != tc.expected {
t.Errorf("Template generated wrong config, got %v but expected %v.", buf.String(), tc.expected)
}
}
}

func newSplitTemplate(t *testing.T) *template.Template {
t.Helper()
tmpl, err := template.New("testTemplate").Funcs(helperFunctions).Parse(`{{range $n := split . ","}}{{$n}} {{end}}`)
Expand All @@ -291,3 +430,48 @@ func newTrimTemplate(t *testing.T) *template.Template {
}
return tmpl
}

func newContainsTemplate(t *testing.T) *template.Template {
t.Helper()
tmpl, err := template.New("testTemplate").Funcs(helperFunctions).Parse(`{{contains .InputString .Substring}}`)
if err != nil {
t.Fatalf("Failed to parse template: %v", err)
}
return tmpl
}

func newHasPrefixTemplate(t *testing.T) *template.Template {
t.Helper()
tmpl, err := template.New("testTemplate").Funcs(helperFunctions).Parse(`{{hasPrefix .InputString .Prefix}}`)
if err != nil {
t.Fatalf("Failed to parse template: %v", err)
}
return tmpl
}

func newHasSuffixTemplate(t *testing.T) *template.Template {
t.Helper()
tmpl, err := template.New("testTemplate").Funcs(helperFunctions).Parse(`{{hasSuffix .InputString .Suffix}}`)
if err != nil {
t.Fatalf("Failed to parse template: %v", err)
}
return tmpl
}

func newToLowerTemplate(t *testing.T) *template.Template {
t.Helper()
tmpl, err := template.New("testTemplate").Funcs(helperFunctions).Parse(`{{toLower .InputString}}`)
if err != nil {
t.Fatalf("Failed to parse template: %v", err)
}
return tmpl
}

func newToUpperTemplate(t *testing.T) *template.Template {
t.Helper()
tmpl, err := template.New("testTemplate").Funcs(helperFunctions).Parse(`{{toUpper .InputString}}`)
if err != nil {
t.Fatalf("Failed to parse template: %v", err)
}
return tmpl
}
15 changes: 5 additions & 10 deletions internal/configs/version2/template_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,12 @@ func hasCIKey(key string, d map[string]string) bool {
return ok
}

// toLower takes a string and make it lowercase.
//
// Example:
//
// {{ if .SameSite}} samesite={{.SameSite | toLower }}{{ end }}
func toLower(s string) string {
return strings.ToLower(s)
}
jjngx marked this conversation as resolved.
Show resolved Hide resolved

var helperFunctions = template.FuncMap{
"headerListToCIMap": headerListToCIMap,
"hasCIKey": hasCIKey,
"toLower": toLower,
"contains": strings.Contains,
"hasPrefix": strings.HasPrefix,
"hasSuffix": strings.HasSuffix,
"toLower": strings.ToLower,
"toUpper": strings.ToUpper,
}
Loading
Loading