Skip to content

Commit

Permalink
Handle legacy
Browse files Browse the repository at this point in the history
  • Loading branch information
speedfl committed Jul 20, 2022
1 parent 5d0b985 commit 29c42bd
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 2 deletions.
72 changes: 70 additions & 2 deletions applicationset/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"sort"
"strings"

Expand Down Expand Up @@ -37,7 +38,13 @@ func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *
return nil, err
}

template, err := template.New(tmpl.Name).Parse(string(tmplBytes))
finalTemplate, err := r.getTemplate(string(tmplBytes))

if err != nil {
return nil, err
}

template, err := template.New(tmpl.Name).Parse(finalTemplate)

if err != nil {
return nil, err
Expand Down Expand Up @@ -69,11 +76,72 @@ func (r *Render) RenderTemplateParams(tmpl *argoappsv1.Application, syncPolicy *
return &replacedTmpl, nil
}

// Check if input is matching Go Template
func (r *Render) isMatchingGoTemplate(input string) bool {
match := regexp.MustCompile(`{{\s*-?(?:and |call |html |index |slice |js |len |not |or |print |printf |println |urlquery |eq |ge |gt |le |lt |ne |block |break |continue |define |else |end |if |range |nil |template |with |".*?"|\$|.*?\(.*?\)|\.).*?-?}}`).Find([]byte(input))
return match != nil
}

// For backward compatibility, ensure previous flat templating is still working by performing following changes:
// {{ path[n] }} => {{ .path.segments[n] }}
// {{ path }} => {{ .path.path }}
// {{ anything.with.a.dot }} => {{ .anything.with.a.dot }}
func (r *Render) replaceLegacyFlat(input string) string {

var tmp string

// Step 1: {{ path[n] }} to {{ .path.segments[n] }}
rePathSegments := regexp.MustCompile(`{{\s*path\[(.*?)\]\s*}}`)
tmp = rePathSegments.ReplaceAllString(input, "{{ .path.segments[${1}] }}")

// Step 2: {{ path }} to {{ .path.path }}
rePath := regexp.MustCompile(`{{\s*path\s*}}`)
tmp = rePath.ReplaceAllString(tmp, "{{ .path.path }}")

// Step 3: {{ anything.with.a.dot }} => {{ .anything.with.a.dot }}
// golang does not negative support lookahead, so cannot use {{\s*(?!\.).*?\s*}}
// will search all match of {{\s*(.*)\s*}} and if there is no "." do the replacement
reAll := regexp.MustCompile(`{{\s*(.*?)?\s*}}`)
matches := reAll.FindAllStringSubmatch(tmp, -1)

if matches == nil {
return tmp
}

for _, value := range matches {
if !strings.HasPrefix(value[1], ".") {
reReplace := regexp.MustCompile(fmt.Sprintf(`{{\s*(%s)\s*}}`, value[1]))
tmp = reReplace.ReplaceAllString(tmp, "{{ .${1} }}")
}
}

return tmp
}

// check if template is Go Template like
// if yes returns it as it
// if not apply logic to replace legacy params
func (r *Render) getTemplate(input string) (string, error) {

if r.isMatchingGoTemplate(input) {
return input, nil
}

return r.replaceLegacyFlat(input), nil
}

// Replace executes basic string substitution of a template with replacement values.
// 'allowUnresolved' indicates whether it is acceptable to have unresolved variables
// remaining in the substituted template.
func (r *Render) Replace(tmpl string, replaceMap any) (string, error) {
template, err := template.New("").Parse(tmpl)

finalTemplate, err := r.getTemplate(tmpl)

if err != nil {
return "", err
}

template, err := template.New("").Parse(finalTemplate)

if err != nil {
return "", err
Expand Down
81 changes: 81 additions & 0 deletions applicationset/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,88 @@ func TestRenderTemplateParams(t *testing.T) {
}
})
}
}

func TestBackwardCompatibilityFastTemplate(t *testing.T) {

tests := []struct {
name string
fieldVal string
expectedVal string
}{
{
name: "Legacy flattemplate Basic",
fieldVal: "{{metadata.labels.app}}",
expectedVal: "{{ .metadata.labels.app }}",
},
{
name: "Legacy flattemplate Basic 2",
fieldVal: "{{ metadata.labels.app }}",
expectedVal: "{{ .metadata.labels.app }}",
},
{
name: "Legacy flattemplate Basic 3",
fieldVal: "{{metadata.labels.app }}",
expectedVal: "{{ .metadata.labels.app }}",
},
{
name: "Legacy flattemplate Basic 4",
fieldVal: "{{ metadata.labels.app}}",
expectedVal: "{{ .metadata.labels.app }}",
},
{
name: "Legacy flattemplate Multiple",
fieldVal: "{{metadata.labels.app}} {{metadata.labels.environment}}",
expectedVal: "{{ .metadata.labels.app }} {{ .metadata.labels.environment }}",
},
{
name: "Legacy flattemplate Multiple 2",
fieldVal: "{{ metadata.labels.app }} {{ metadata.labels.environment }}",
expectedVal: "{{ .metadata.labels.app }} {{ .metadata.labels.environment }}",
},
{
name: "Legacy flattemplate Multiple 3",
fieldVal: "{{ metadata.labels.app }} {{ metadata.labels.environment }} {{ metadata.labels.app }} {{ metadata.labels.environment }}",
expectedVal: "{{ .metadata.labels.app }} {{ .metadata.labels.environment }} {{ .metadata.labels.app }} {{ .metadata.labels.environment }}",
},
{
name: "Legacy flattemplate path",
fieldVal: "{{ path }}",
expectedVal: "{{ .path.path }}",
},
{
name: "Legacy flattemplate path[n]",
fieldVal: "{{ path[0] }}/{{ path[1] }}",
expectedVal: "{{ .path.segments[0] }}/{{ .path.segments[1] }}",
},
{
name: "Usage of go template method (if)",
fieldVal: "{{ if .metadata.labels.app }} {{ .metadata.labels.app }} {{ - end - }}",
expectedVal: "{{ if .metadata.labels.app }} {{ .metadata.labels.app }} {{ - end - }}",
},
{
name: "Usage of go template method (if 2)",
fieldVal: "{{ - if .metadata.labels.app }} {{ .metadata.labels.app }} {{ - end - }}",
expectedVal: "{{ - if .metadata.labels.app }} {{ .metadata.labels.app }} {{ - end - }}",
},
{
name: "Usage of go template method (if 3)",
fieldVal: "{{ if .metadata.labels.app - }} {{ .metadata.labels.app }} {{ - end - }}",
expectedVal: "{{ if .metadata.labels.app - }} {{ .metadata.labels.app }} {{ - end - }}",
},
{
name: "Usage of go template methods (range)",
fieldVal: "{{ range $key, $value := .metadata.labels }} {{ $key }}: {{ $value }} ($.other.test) {{ - end - }}",
expectedVal: "{{ range $key, $value := .metadata.labels }} {{ $key }}: {{ $value }} ($.other.test) {{ - end - }}",
},
}

for _, test := range tests {
render := Render{}
actual, err := render.getTemplate(test.fieldVal)
assert.Nil(t, err, "Test '%s' failed. expectedVal no error but error detected, %s", test.name, err)
assert.Equal(t, test.expectedVal, actual, "Test '%s' failed. expectedVal '%s' but got '%s'", test.name, test.expectedVal, actual)
}
}

func TestRenderTemplateParamsFinalizers(t *testing.T) {
Expand Down

0 comments on commit 29c42bd

Please sign in to comment.