diff --git a/builtin/providers/template/datasource_template_file.go b/builtin/providers/template/datasource_template_file.go index 865a24e06..6a04ccfca 100644 --- a/builtin/providers/template/datasource_template_file.go +++ b/builtin/providers/template/datasource_template_file.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/hashicorp/hil" "github.com/hashicorp/hil/ast" @@ -49,10 +50,11 @@ func dataSourceFile() *schema.Resource { ConflictsWith: []string{"template"}, }, "vars": &schema.Schema{ - Type: schema.TypeMap, - Optional: true, - Default: make(map[string]interface{}), - Description: "variables to substitute", + Type: schema.TypeMap, + Optional: true, + Default: make(map[string]interface{}), + Description: "variables to substitute", + ValidateFunc: validateVarsAttribute, }, "rendered": &schema.Schema{ Type: schema.TypeString, @@ -156,3 +158,22 @@ func validateTemplateAttribute(v interface{}, key string) (ws []string, es []err return } + +func validateVarsAttribute(v interface{}, key string) (ws []string, es []error) { + // vars can only be primitives right now + var badVars []string + for k, v := range v.(map[string]interface{}) { + switch v.(type) { + case []interface{}: + badVars = append(badVars, fmt.Sprintf("%s (list)", k)) + case map[string]interface{}: + badVars = append(badVars, fmt.Sprintf("%s (map)", k)) + } + } + if len(badVars) > 0 { + es = append(es, fmt.Errorf( + "%s: cannot contain non-primitives; bad keys: %s", + key, strings.Join(badVars, ", "))) + } + return +} diff --git a/builtin/providers/template/datasource_template_file_test.go b/builtin/providers/template/datasource_template_file_test.go index 64a64102a..5e82382a6 100644 --- a/builtin/providers/template/datasource_template_file_test.go +++ b/builtin/providers/template/datasource_template_file_test.go @@ -71,6 +71,49 @@ func TestValidateTemplateAttribute(t *testing.T) { } } +func TestValidateVarsAttribute(t *testing.T) { + cases := map[string]struct { + Vars map[string]interface{} + ExpectErr string + }{ + "lists are invalid": { + map[string]interface{}{ + "list": []interface{}{}, + }, + `vars: cannot contain non-primitives`, + }, + "maps are invalid": { + map[string]interface{}{ + "map": map[string]interface{}{}, + }, + `vars: cannot contain non-primitives`, + }, + "strings, integers, floats, and bools are AOK": { + map[string]interface{}{ + "string": "foo", + "int": 1, + "bool": true, + "float": float64(1.0), + }, + ``, + }, + } + + for tn, tc := range cases { + _, es := validateVarsAttribute(tc.Vars, "vars") + if len(es) > 0 { + if tc.ExpectErr == "" { + t.Fatalf("%s: expected no err, got: %#v", tn, es) + } + if !strings.Contains(es[0].Error(), tc.ExpectErr) { + t.Fatalf("%s: expected\n%s\nto contain\n%s", tn, es[0], tc.ExpectErr) + } + } else if tc.ExpectErr != "" { + t.Fatalf("%s: expected err containing %q, got none!", tn, tc.ExpectErr) + } + } +} + // This test covers a panic due to config.Func formerly being a // shared map, causing multiple template_file resources to try and // accessing it parallel during their lang.Eval() runs. diff --git a/website/source/docs/providers/template/d/file.html.md b/website/source/docs/providers/template/d/file.html.md index 6b8381d61..b47f7e286 100644 --- a/website/source/docs/providers/template/d/file.html.md +++ b/website/source/docs/providers/template/d/file.html.md @@ -31,7 +31,9 @@ The following arguments are supported: from a file on disk using the [`file()` interpolation function](/docs/configuration/interpolation.html#file_path_). -* `vars` - (Optional) Variables for interpolation within the template. +* `vars` - (Optional) Variables for interpolation within the template. Note + that variables must all be primitives. Direct references to lists or maps + will cause a validation error. The following arguments are maintained for backwards compatibility and may be removed in a future version: