-
Notifications
You must be signed in to change notification settings - Fork 9.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
String templates as values #28700
Closed
Closed
String templates as values #28700
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is a design prototype of one possible way to meet the need of defining a template in a different location from where it will ultimately be evaluated. For example, a generic module might accept a template as an input variable and then evaluate that template using values that are private to the module, thus separating the concern of providing the data from the concern of assembling the data into a particular string shape. This is only just enough to try out the behavior and see how it feels and how intuitive it seems. If we were to move forward with something like this then a real implementation would need some extra care to some other concerns, including but not limited to: - Generating good error messages when templates don't make valid use of their arguments. - Properly blocking template values from being used in places where they don't make sense, such as as input to jsonencode(...), or as a root module output value. - Possibly a function similar to templatefile which can create a template value from a file on disk, to allow factoring out larger templates while still being compatible with modules that expect template values.
apparentlymart
added a commit
that referenced
this pull request
May 14, 2021
At the time of this commit we have a proposal #28700 which would, if accepted, need to reserve a new reference prefix to represent template arguments. It seems unlikely that the proposal would be accepted and implemented before Terraform v1.0 creates additional compatibility constraints, and so this pre-emptively reserves a few candidate symbol names to allow something like that proposal to potentially move forward later without requiring a new opt-in language edition. If we do move forward with the proposal then we'll select one of these three reserved names depending on which form of the proposal we decide to move forward with, and then un-reserve the other two. If we decide to not pursue this proposal at all then we'll un-reserve all three once that decision is finalized. It's unlikely that there is any existing provider which has a resource type named either "template", "lazy", or "arg", but in that unlikely event users of that provider can keep using it by adding the "resource." escaping prefix, such as changing "lazy.foo.bar" into "resource.lazy.foo.bar".
apparentlymart
added a commit
that referenced
this pull request
May 17, 2021
At the time of this commit we have a proposal #28700 which would, if accepted, need to reserve a new reference prefix to represent template arguments. It seems unlikely that the proposal would be accepted and implemented before Terraform v1.0 creates additional compatibility constraints, and so this pre-emptively reserves a few candidate symbol names to allow something like that proposal to potentially move forward later without requiring a new opt-in language edition. If we do move forward with the proposal then we'll select one of these three reserved names depending on which form of the proposal we decide to move forward with, and then un-reserve the other two. If we decide to not pursue this proposal at all then we'll un-reserve all three once that decision is finalized. It's unlikely that there is any existing provider which has a resource type named either "template", "lazy", or "arg", but in that unlikely event users of that provider can keep using it by adding the "resource." escaping prefix, such as changing "lazy.foo.bar" into "resource.lazy.foo.bar".
apparentlymart
added a commit
that referenced
this pull request
May 17, 2021
At the time of this commit we have a proposal #28700 which would, if accepted, need to reserve a new reference prefix to represent template arguments. It seems unlikely that the proposal would be accepted and implemented before Terraform v1.0 creates additional compatibility constraints, and so this pre-emptively reserves a few candidate symbol names to allow something like that proposal to potentially move forward later without requiring a new opt-in language edition. If we do move forward with the proposal then we'll select one of these three reserved names depending on which form of the proposal we decide to move forward with, and then un-reserve the other two. If we decide to not pursue this proposal at all then we'll un-reserve all three once that decision is finalized. It's unlikely that there is any existing provider which has a resource type named either "template", "lazy", or "arg", but in that unlikely event users of that provider can keep using it by adding the "resource." escaping prefix, such as changing "lazy.foo.bar" into "resource.lazy.foo.bar".
I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions. |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This is a design prototype of one possible way to meet the need of defining a template in a different location from where it will ultimately be evaluated, as discussed in #26838. For example, a generic module might accept a template as an input variable and then evaluate that template using values that are private to the module, thus separating the concern of providing the data from the concern of assembling the data into a particular string shape.
The idea here is to establish a new type of value in the Terraform language that can store a template that has been parsed but not yet evaluated. This is similar in principle to a normal string containing template syntax passed into the deprecated
template_file
data source, but allows use of the real template syntax at the definition site and allows for early evaluation of syntax. It also, unlike a string passed totemplate_file
, allows the template to refer to a mixture of template arguments and other values from the surrounding scope.For a small example I put together something admittedly-contrived around constructing log line prefixes. This particular use-case probably doesn't make sense to be done at the Terraform level because log lines tend to incorporate very dynamic data about the specific system generating them, but it serves to show the overall mechanics of this design nonetheless.
The module which consumes the template looks like this:
This shows two different aspects of this proposed design:
template
type constraint syntax, which represents accepting a template that will have a particular set of arguments available to it.evaltemplate
function, which takes a value of a template type and an object whose attributes conform to the template's argument types and evaluates the template, returning the resulting string.The actual definition of the template is left to the calling module, whose source code might look something like this:
Again, this shows an aspect of the proposed design:
maketemplate
function, which parses its argument as a string template (using a custom argument parser technique, similar to how Terraform currently implements thetry
andcan
functions) and saves it along with the evaluation scope inside a value of a template type, which it returns.The
maketemplate
function treats its argument as a normal string template, except that it additionally supports special symbols with thetemplate.
prefix, which serve as the template's arguments. Notice that thelog_prefix_template
template above includes bothtemplate.
-prefixed references and a normalvar.env_name
prefix, which allows it to create a blend of values known both in the calling module and in the called module.The result of my contrived configuration here is the following:
Although this valid example doesn't really show it, having an explicit type for templates (as opposed to just passing them as strings with special characters inside) allows various validity checks on both the side of the template definer and the template evaluator, helping both to cooperate to produce a working result. For example, if I change the template to include a reference to a template argument that the module doesn't provide, we can catch that early at the call site:
Or, if I change the template to use one of the arguments in a way that isn't compatible with the argument types the module defines:
On the evaluator side, similarly we can catch if the
templateeval
call is missing one of the expected arguments or if one has been assigned an incompatible type, even if the currently-passed template value doesn't make use of all of the arguments or if we're just validating the shared module in isolation:(The basic error messages above are just placeholders for more detailed messages we should implement if we decide to move forward with this.)
This is only just enough to try out the behavior and see how it feels and how intuitive it seems. If we were to move forward with something like this then a real implementation would need some extra care to some other concerns, including but not limited to:
Generating good error messages when templates don't make valid use of their arguments.
Properly blocking template values from being used in places where they don't make sense, such as as input to
jsonencode(...)
, as a root module output value, or directly as an argument to a provider. (In all of these situations, it's necessary to first evaluate the template and pass the resulting string.)Possibly another function similar to
templatefile
which can create a template value from a file on disk, to allow factoring out larger templates while still being compatible with modules that expect template values.Defining default values for variables with template type constraints given that functions aren't available in the
default
argument, and thus it wouldn't naturally work to callmaketemplate
from in there.There are undoubtedly more things which we'd find with further prototyping and experimentation, if this design seems to have promise.
Before deciding whether to move forward with anything like this we'll need to evaluate it against various use-cases and see which ones it supports and which it doesn't. It doesn't necessarily need to solve all use-cases related to templating, but in order to be a satisfying replacement for
template_file
it should at least address use-cases involving a template being defined in a different module than where it's being evaluated.