lang: stabilise templatestring func experiment (#35224)

* lang: stabilise templatestring func experiment

* command/jsonfunction: marshal templatestring

* docs: add templatestring
pull/35261/head
kmoe 2 years ago committed by GitHub
parent b5d6b13b3b
commit 1e2d4a2ecc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -86,6 +86,8 @@ func Marshal(f map[string]function.Function) ([]byte, tfdiags.Diagnostics) {
signatures.Signatures[name] = marshalCan(v)
} else if name == "try" || name == "core::try" {
signatures.Signatures[name] = marshalTry(v)
} else if name == "templatestring" || name == "core::templatestring" {
signatures.Signatures[name] = marshalTemplatestring(v)
} else {
signature, err := marshalFunction(v)
if err != nil {
@ -194,3 +196,28 @@ func marshalCan(can function.Function) *FunctionSignature {
},
}
}
// marshalTemplatestring returns a static function signature for the
// templatestring function.
// We need this exception because the function implementation uses capsule
// types that we can't marshal.
func marshalTemplatestring(templatestring function.Function) *FunctionSignature {
return &FunctionSignature{
Description: templatestring.Description(),
ReturnType: cty.String,
Parameters: []*parameter{
{
Name: templatestring.Params()[0].Name,
Description: templatestring.Params()[0].Description,
IsNullable: templatestring.Params()[0].AllowNull,
Type: cty.String,
},
{
Name: templatestring.Params()[1].Name,
Description: templatestring.Params()[1].Description,
IsNullable: templatestring.Params()[1].AllowNull,
Type: cty.DynamicPseudoType,
},
},
}
}

@ -33,7 +33,7 @@ func init() {
registerConcludedExperiment(VariableValidation, "Custom variable validation can now be used by default, without enabling an experiment.")
registerConcludedExperiment(VariableValidationCrossRef, "Input variable validation rules may now refer to other objects in the same module without enabling any experiment.")
registerConcludedExperiment(SuppressProviderSensitiveAttrs, "Provider-defined sensitive attributes are now redacted by default, without enabling an experiment.")
registerCurrentExperiment(TemplateStringFunc)
registerConcludedExperiment(TemplateStringFunc, "The templatestring function can now be used without enabling an experiment.")
registerConcludedExperiment(ConfigDrivenMove, "Declarations of moved resource instances using \"moved\" blocks can now be used by default, without enabling an experiment.")
registerConcludedExperiment(PreconditionsPostconditions, "Condition blocks can now be used by default, without enabling an experiment.")
registerConcludedExperiment(ModuleVariableOptionalAttrs, "The final feature corresponding to this experiment differs from the experimental form and is available in the Terraform language from Terraform v1.3.0 onwards.")

@ -423,8 +423,8 @@ var DescriptionList = map[string]descriptionEntry{
"templatestring": {
Description: "`templatestring` takes a string from elsewhere in the module and renders its content as a template using a supplied set of template variables.",
ParamDescription: []string{
"a simple reference to a string value containing the template source code",
"object of variables to expose in the template scope",
"A simple reference to a string value containing the template source code.",
"Object of variables to expose in the template scope.",
},
},
"textdecodebase64": {

@ -190,9 +190,7 @@ func (s *Scope) Functions() map[string]function.Function {
return s.funcs, filesystemFunctions, templateFunctions
}
coreFuncs["templatefile"] = funcs.MakeTemplateFileFunc(s.BaseDir, funcsFunc)
if s.activeExperiments.Has(experiments.TemplateStringFunc) {
coreFuncs["templatestring"] = funcs.MakeTemplateStringFunc(funcsFunc)
}
coreFuncs["templatestring"] = funcs.MakeTemplateStringFunc(funcsFunc)
if s.ConsoleMode {
// The type function is only available in terraform console.

@ -13,6 +13,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/experiments"
"github.com/hashicorp/terraform/internal/lang/marks"
homedir "github.com/mitchellh/go-homedir"
@ -976,6 +977,21 @@ func TestFunctions(t *testing.T) {
},
},
"templatestring": {
{
`templatestring(local.greeting_template, {
name = "Arthur"
})`,
cty.StringVal("Hello, Arthur!"),
},
{
`core::templatestring(local.greeting_template, {
name = "Namespaced Arthur"
})`,
cty.StringVal("Hello, Namespaced Arthur!"),
},
},
"timeadd": {
{
`timeadd("2017-11-22T00:00:00Z", "1s")`,
@ -1311,9 +1327,14 @@ func TestFunctions(t *testing.T) {
for _, test := range funcTests {
t.Run(test.src, func(t *testing.T) {
data := &dataForTests{} // no variables available; we only need literals here
data := &dataForTests{
LocalValues: map[string]cty.Value{
"greeting_template": cty.StringVal("Hello, ${name}!"),
},
}
scope := &Scope{
Data: data,
ParseRef: addrs.ParseRef,
BaseDir: "./testdata/functions-test", // for the functions that read from the filesystem
PlanTimestamp: time.Date(2004, 04, 25, 15, 00, 00, 000, time.UTC),
ExternalFuncs: externalFuncs,

@ -365,6 +365,10 @@
"title": "<code>substr</code>",
"href": "/language/functions/substr"
},
{
"title": "<code>templatestring</code>",
"href": "/language/functions/templatestring"
},
{
"title": "<code>title</code>",
"href": "/language/functions/title"
@ -893,6 +897,11 @@
"path": "functions/templatefile",
"hidden": true
},
{
"title": "templatestring",
"path": "functions/templatestring",
"hidden": true
},
{ "title": "terraform-encode_tfvars", "path": "functions/terraform-encode_tfvars", "hidden": true },
{ "title": "terraform-decode_tfvars", "path": "functions/terraform-decode_tfvars", "hidden": true },
{ "title": "terraform-encode_expr", "path": "functions/terraform-encode_expr", "hidden": true },

@ -148,3 +148,4 @@ For more information, see the main documentation for
* [`file`](/terraform/language/functions/file) reads a file from disk and returns its literal contents
without any template interpretation.
* [`templatestring`](/terraform/language/functions/templatestring) takes a simple reference to a string value containing the template and renders its content.

@ -0,0 +1,67 @@
---
page_title: templatestring - Functions - Configuration Language
description: |-
The templatestring function takes a string from elsewhere in the module and renders its content as a template using a supplied set of template variables.
---
# `templatestring` Function
-> **Note:** The `templatestring` function is intended for advanced use cases. Most use cases require only a [string template expression](/terraform/language/expressions/strings#string-templates). To render a template from a file, use the [`templatefile` function](/terraform/language/functions/templatefile).
`templatestring` renders a template using a supplied set of template variables.
```hcl
templatefile(ref, vars)
```
The first parameter must be a simple reference to string value containing the template: for example, `data.aws_s3_object.example.body` or `local.inline_template`.
It is **not** valid to supply the template expression directly as the first argument:
```hcl
# The following is not allowed
templatestring("Hello, $${name}", {
name = var.name
})
```
Instead of the above, you should instead use a string template expression:
```hcl
"Hello, ${var.name}"
```
The `templatestring` function is needed only when the template is available as a named object in the current module.
The template syntax is the same as for
[string templates](/terraform/language/expressions/strings#string-templates)
in the main Terraform language, including interpolation sequences delimited with
`${` ... `}`.
Strings in the Terraform language are sequences of Unicode characters, so
this function will interpret the file contents as UTF-8 encoded text and
return the resulting Unicode characters. If the template contains invalid UTF-8
sequences then this function will produce an error.
## Example
The following example retrieves a template from S3 and dynamically renders it:
```hcl
data "aws_s3_object" "example" {
bucket = "example-example"
key = "example.tmpl"
}
output "example" {
value = templatestring(data.aws_s3_object.example.body, {
name = var.name
})
}
```
For more examples of how to use templates, please see the documentation for the [`templatefile`](/terraform/language/functions/templatefile#Examples) function.
## Related Functions
* [`templatefile`](/terraform/language/functions/templatefile) reads a file from disk and renders its content as a template.
Loading…
Cancel
Save