Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.6.1-dev
v0.7.0
81 changes: 81 additions & 0 deletions internal/template/delimiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package template

import (
"encoding/json"
"errors"
"strings"
)

const (
prefixBootstrap = "#?bootstrap"
defaultStart = "{{"
defaultEnd = "}}"
)

type Delimiter struct {
Start string
End string
}

type DelimiterJson struct {
Template struct {
Delims struct {
Start string `json:"start"`
End string `json:"end"`
} `json:"delims"`
} `json:"template"`
}

type DelimiterConfig struct {
Template string
}

func NewDelimiterConfig(template string) *DelimiterConfig {
return &DelimiterConfig{
Template: template,
}
}

// ParseAndCleanup parses the delimiter configuration from the template string
// and returns the cleaned-up template string along with the Delimiter, or an error if parsing fails.
// It always returns a Delimiter, defaulting to "{{" and "}}" if no configuration is found.
// Delimiter configuration string needs to be in the first line of the template, e.g.:
// #?bootstrap {"template": {"delims": {"start": "<<", "end": ">>"}}}
func (d *DelimiterConfig) ParseAndCleanup() (string, *Delimiter, error) {
if !strings.HasPrefix(d.Template, prefixBootstrap) {
return d.Template, &Delimiter{Start: defaultStart, End: defaultEnd}, nil
}

template := strings.TrimSpace(d.Template)
firstLineEnd := strings.Index(template, "\n")
if firstLineEnd == -1 {
firstLineEnd = len(template)
}

jsonPart := strings.TrimSpace(strings.TrimPrefix(template[:firstLineEnd], prefixBootstrap))
if jsonPart == "" {
return "", nil, errors.New("invalid template delimiter configuration")
}
var config DelimiterJson
if err := json.Unmarshal([]byte(jsonPart), &config); err != nil {
return "", nil, errors.New("cannot parse detected template delimiter configuration")
}

template = removeBootstrapConfig(template)

return template, &Delimiter{Start: config.Template.Delims.Start, End: config.Template.Delims.End}, nil
}

// removeBootstrapConfig removes the bootstrap configuration line from the template string.
func removeBootstrapConfig(template string) string {
lines := strings.Split(template, "\n")
var cleanLines []string

for _, line := range lines {
trimmed := strings.TrimSpace(line)
if !strings.HasPrefix(trimmed, prefixBootstrap) {
cleanLines = append(cleanLines, line)
}
}
return strings.Join(cleanLines, "\n")
}
60 changes: 60 additions & 0 deletions internal/template/delimiter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package template

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestDelimiterConfig_parse(t *testing.T) {
tests := []struct {
name string
template string
wantTemplate string
wantDelimiter *Delimiter
wantErr assert.ErrorAssertionFunc
}{
{
"Valid Delimiter Configuration",
`#?bootstrap {"template": {"delims": {"start": "<<", "end": ">>"}}}
apiVersion: v1`,
"apiVersion: v1",
&Delimiter{Start: "<<", End: ">>"},
assert.NoError,
},
{
"Invalid JSON Configuration",
`#?bootstrap {"template": {"delims": {"start": "<<", "end": ">>"}`,
"",
nil,
assert.Error,
},
{
"Missing JSON Configuration",
`#?bootstrap`,
"",
nil,
assert.Error,
},
{
"No Delimiter Configuration",
`apiVersion: v1
kind: Pod`,
`apiVersion: v1
kind: Pod`,
&Delimiter{"{{", "}}"},
assert.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
template, delim, err := NewDelimiterConfig(tt.template).ParseAndCleanup()
if !tt.wantErr(t, err) {
return
}
assert.Equalf(t, tt.wantTemplate, template, "template")
assert.Equalf(t, tt.wantDelimiter, delim, "delimiter")

})
}
}
8 changes: 7 additions & 1 deletion internal/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,18 @@ func (t *TemplateExecution) WithOCMComponentGetter(ctx context.Context, compGett
func (t *TemplateExecution) Execute(name, template string, input map[string]interface{}) ([]byte, error) {
tmpl := gotmpl.New(name)

template, delims, err := NewDelimiterConfig(template).ParseAndCleanup()
if err != nil {
return nil, err
}
tmpl = tmpl.Delims(delims.Start, delims.End)

for _, fm := range t.funcMaps {
tmpl.Funcs(fm)
}

tmpl.Option("missingkey=" + t.missingKeyOption)
_, err := tmpl.Parse(template)
_, err = tmpl.Parse(template)
if err != nil {
return nil, TemplateErrorBuilder(err).WithSource(&template).WithInput(input, t.templateInputFormatter).Build()
}
Expand Down