Skip to content

Commit

Permalink
provisioner: add dict template function
Browse files Browse the repository at this point in the history
Add dict template function inspired by
https://stackoverflow.com/questions/18276173/calling-a-template-with-several-pipeline-parameters/18276968#18276968

Compared to stackoverflow version it additionally checks for duplicate keys.

Also decided not to use `dict` from github.com/Masterminds/sprig/v3
as it allows non-string keys, uneven number of arguments and
does not check for duplicate keys.

Signed-off-by: Alexander Yastrebov <[email protected]>
  • Loading branch information
AlexanderYastrebov committed Nov 29, 2023
1 parent ddc8570 commit 8a9d95e
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 0 deletions.
22 changes: 22 additions & 0 deletions provisioner/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ func renderTemplate(context *templateContext, file string) (string, error) {
"sumQuantities": sumQuantities,
"awsValidID": awsValidID,
"indent": sprig.GenericFuncMap()["indent"],
"dict": dict,
}

content, ok := context.fileData[file]
Expand Down Expand Up @@ -285,6 +286,27 @@ func split(s string, d string) []string {
return strings.Split(s, d)
}

// dict is a template function that constructs a map out of its arguments.
// Argument list is treated as a sequence of key-value pairs and must have even length.
// Key arguments must be unique and have string type.
func dict(args ...interface{}) (map[string]interface{}, error) {
if len(args) == 0 || len(args)%2 != 0 {
return nil, fmt.Errorf("dict: invalid number of arguments: %d", len(args))
}
dict := make(map[string]interface{}, len(args)/2)
for i := 0; i < len(args); i += 2 {
key, ok := args[i].(string)
if !ok {
return nil, fmt.Errorf("dict: key argument %d must be string", i)
}
if _, ok := dict[key]; ok {
return nil, fmt.Errorf("dict: duplicate key %s", key)
}
dict[key] = args[i+1]
}
return dict, nil
}

// accountID returns just the ID part of an account
func accountID(account string) (string, error) {
items := strings.Split(account, ":")
Expand Down
37 changes: 37 additions & 0 deletions provisioner/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1164,3 +1164,40 @@ func TestNodePoolGroupsProfile(t *testing.T) {
})
}
}

func TestDict(t *testing.T) {
result, err := renderSingle(
t,
`{{ define "a-template" -}}
name: {{ .name }}
version: {{ .version }}
{{ end }}
{{ template "a-template" dict "name" "foo" "version" .Values.data }}
`,
"1")

require.NoError(t, err)
require.EqualValues(t, `
name: foo
version: 1
`, result)
}

func TestDictInvalidArgs(t *testing.T) {
for i, tc := range []struct {
args []interface{}
}{
{args: []interface{}{}},
{args: []interface{}{"foo"}},
{args: []interface{}{1, "foo"}},
{args: []interface{}{"foo", "bar", "foo", "baz"}},
} {
t.Run(fmt.Sprintf("%d: %v", i, tc.args), func(t *testing.T) {
_, err := dict(tc.args...)
require.Error(t, err)
})
}
}

0 comments on commit 8a9d95e

Please sign in to comment.