lang/funcs: templatefile requires valid variable names

Previously the templatefile function would permit any arbitrary string as
a variable name, but due to the HCL template syntax it would be impossible
to refer to one that isn't a valid HCL identifier without causing an
HCL syntax error.

The HCL syntax errors are correct, but don't really point to the root
cause of the problem. Instead, we'll pre-verify that the variable names
are valid before we even try to render the template, and given a
specialized error message that refers to the vars argument expression as
the problematic part, which will hopefully make the resolution path
clearer for a user encountering this situation.

The syntax error still remains for situations where all of the variable
names are correct but e.g. the user made a typo referring to one, which
makes sense because in that case the problem _is_ inside the template.
This commit is contained in:
Martin Atkins 2020-02-21 16:35:27 -08:00
parent ec9f950b3f
commit 67d95b97ce
3 changed files with 25 additions and 1 deletions

View File

@ -100,6 +100,20 @@ func MakeTemplateFileFunc(baseDir string, funcsCb func() map[string]function.Fun
Variables: varsVal.AsValueMap(), Variables: varsVal.AsValueMap(),
} }
// We require all of the variables to be valid HCL identifiers, because
// otherwise there would be no way to refer to them in the template
// anyway. Rejecting this here gives better feedback to the user
// than a syntax error somewhere in the template itself.
for n := range ctx.Variables {
if !hclsyntax.ValidIdentifier(n) {
// This error message intentionally doesn't describe _all_ of
// the different permutations that are technically valid as an
// HCL identifier, but rather focuses on what we might
// consider to be an "idiomatic" variable name.
return cty.DynamicVal, function.NewArgErrorf(1, "invalid template variable name %q: must start with a letter, followed by zero or more letters, digits, and underscores", n)
}
}
// We'll pre-check references in the template here so we can give a // We'll pre-check references in the template here so we can give a
// more specialized error message than HCL would by default, so it's // more specialized error message than HCL would by default, so it's
// clearer that this problem is coming from a templatefile call. // clearer that this problem is coming from a templatefile call.

View File

@ -86,6 +86,14 @@ func TestTemplateFile(t *testing.T) {
cty.StringVal("Hello, Jodie!"), cty.StringVal("Hello, Jodie!"),
``, ``,
}, },
{
cty.StringVal("testdata/hello.tmpl"),
cty.MapVal(map[string]cty.Value{
"name!": cty.StringVal("Jodie"),
}),
cty.NilVal,
`invalid template variable name "name!": must start with a letter, followed by zero or more letters, digits, and underscores`,
},
{ {
cty.StringVal("testdata/hello.tmpl"), cty.StringVal("testdata/hello.tmpl"),
cty.ObjectVal(map[string]cty.Value{ cty.ObjectVal(map[string]cty.Value{

View File

@ -29,7 +29,9 @@ into a separate file for readability.
The "vars" argument must be a map. Within the template file, each of the keys The "vars" argument must be a map. Within the template file, each of the keys
in the map is available as a variable for interpolation. The template may in the map is available as a variable for interpolation. The template may
also use any other function available in the Terraform language, except that also use any other function available in the Terraform language, except that
recursive calls to `templatefile` are not permitted. recursive calls to `templatefile` are not permitted. Variable names must
each start with a letter, followed by zero or more letters, digits, or
underscores.
Strings in the Terraform language are sequences of Unicode characters, so Strings in the Terraform language are sequences of Unicode characters, so
this function will interpret the file contents as UTF-8 encoded text and this function will interpret the file contents as UTF-8 encoded text and