Merge pull request #3909 from hashicorp/phinze/template-file-contents
template_file: source contents instead of path
This commit is contained in:
commit
993ec0a320
|
@ -4,7 +4,6 @@ import (
|
|||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -12,8 +11,8 @@ import (
|
|||
"github.com/hashicorp/terraform/config"
|
||||
"github.com/hashicorp/terraform/config/lang"
|
||||
"github.com/hashicorp/terraform/config/lang/ast"
|
||||
"github.com/hashicorp/terraform/helper/pathorcontents"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
func resource() *schema.Resource {
|
||||
|
@ -24,13 +23,23 @@ func resource() *schema.Resource {
|
|||
Read: Read,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"template": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Description: "Contents of the template",
|
||||
ForceNew: true,
|
||||
ConflictsWith: []string{"filename"},
|
||||
},
|
||||
"filename": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Optional: true,
|
||||
Description: "file to read template from",
|
||||
ForceNew: true,
|
||||
// Make a "best effort" attempt to relativize the file path.
|
||||
StateFunc: func(v interface{}) string {
|
||||
if v == nil || v.(string) == "" {
|
||||
return ""
|
||||
}
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return v.(string)
|
||||
|
@ -41,6 +50,8 @@ func resource() *schema.Resource {
|
|||
}
|
||||
return rel
|
||||
},
|
||||
Deprecated: "Use the 'template' attribute instead.",
|
||||
ConflictsWith: []string{"template"},
|
||||
},
|
||||
"vars": &schema.Schema{
|
||||
Type: schema.TypeMap,
|
||||
|
@ -96,23 +107,21 @@ func Read(d *schema.ResourceData, meta interface{}) error {
|
|||
|
||||
type templateRenderError error
|
||||
|
||||
var readfile func(string) ([]byte, error) = ioutil.ReadFile // testing hook
|
||||
|
||||
func render(d *schema.ResourceData) (string, error) {
|
||||
template := d.Get("template").(string)
|
||||
filename := d.Get("filename").(string)
|
||||
vars := d.Get("vars").(map[string]interface{})
|
||||
|
||||
path, err := homedir.Expand(filename)
|
||||
if template == "" && filename != "" {
|
||||
template = filename
|
||||
}
|
||||
|
||||
contents, _, err := pathorcontents.Read(template)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buf, err := readfile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rendered, err := execute(string(buf), vars)
|
||||
rendered, err := execute(contents, vars)
|
||||
if err != nil {
|
||||
return "", templateRenderError(
|
||||
fmt.Errorf("failed to render %v: %v", filename, err),
|
||||
|
|
|
@ -26,15 +26,10 @@ func TestTemplateRendering(t *testing.T) {
|
|||
|
||||
for _, tt := range cases {
|
||||
r.Test(t, r.TestCase{
|
||||
PreCheck: func() {
|
||||
readfile = func(string) ([]byte, error) {
|
||||
return []byte(tt.template), nil
|
||||
}
|
||||
},
|
||||
Providers: testProviders,
|
||||
Steps: []r.TestStep{
|
||||
r.TestStep{
|
||||
Config: testTemplateConfig(tt.vars),
|
||||
Config: testTemplateConfig(tt.template, tt.vars),
|
||||
Check: func(s *terraform.State) error {
|
||||
got := s.RootModule().Outputs["rendered"]
|
||||
if tt.want != got {
|
||||
|
@ -62,14 +57,7 @@ func TestTemplateVariableChange(t *testing.T) {
|
|||
var testSteps []r.TestStep
|
||||
for i, step := range steps {
|
||||
testSteps = append(testSteps, r.TestStep{
|
||||
PreConfig: func(template string) func() {
|
||||
return func() {
|
||||
readfile = func(string) ([]byte, error) {
|
||||
return []byte(template), nil
|
||||
}
|
||||
}
|
||||
}(step.template),
|
||||
Config: testTemplateConfig(step.vars),
|
||||
Config: testTemplateConfig(step.template, step.vars),
|
||||
Check: func(i int, want string) r.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
got := s.RootModule().Outputs["rendered"]
|
||||
|
@ -88,14 +76,13 @@ func TestTemplateVariableChange(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func testTemplateConfig(vars string) string {
|
||||
return `
|
||||
resource "template_file" "t0" {
|
||||
filename = "mock"
|
||||
vars = ` + vars + `
|
||||
}
|
||||
output "rendered" {
|
||||
value = "${template_file.t0.rendered}"
|
||||
}
|
||||
`
|
||||
func testTemplateConfig(template, vars string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "template_file" "t0" {
|
||||
template = "%s"
|
||||
vars = %s
|
||||
}
|
||||
output "rendered" {
|
||||
value = "${template_file.t0.rendered}"
|
||||
}`, template, vars)
|
||||
}
|
||||
|
|
|
@ -76,6 +76,13 @@ type SelfVariable struct {
|
|||
key string
|
||||
}
|
||||
|
||||
// SimpleVariable is an unprefixed variable, which can show up when users have
|
||||
// strings they are passing down to resources that use interpolation
|
||||
// internally. The template_file resource is an example of this.
|
||||
type SimpleVariable struct {
|
||||
Key string
|
||||
}
|
||||
|
||||
// A UserVariable is a variable that is referencing a user variable
|
||||
// that is inputted from outside the configuration. This looks like
|
||||
// "${var.foo}"
|
||||
|
@ -97,6 +104,8 @@ func NewInterpolatedVariable(v string) (InterpolatedVariable, error) {
|
|||
return NewUserVariable(v)
|
||||
} else if strings.HasPrefix(v, "module.") {
|
||||
return NewModuleVariable(v)
|
||||
} else if !strings.ContainsRune(v, '.') {
|
||||
return NewSimpleVariable(v)
|
||||
} else {
|
||||
return NewResourceVariable(v)
|
||||
}
|
||||
|
@ -227,6 +236,18 @@ func (v *SelfVariable) GoString() string {
|
|||
return fmt.Sprintf("*%#v", *v)
|
||||
}
|
||||
|
||||
func NewSimpleVariable(key string) (*SimpleVariable, error) {
|
||||
return &SimpleVariable{key}, nil
|
||||
}
|
||||
|
||||
func (v *SimpleVariable) FullKey() string {
|
||||
return v.Key
|
||||
}
|
||||
|
||||
func (v *SimpleVariable) GoString() string {
|
||||
return fmt.Sprintf("*%#v", *v)
|
||||
}
|
||||
|
||||
func NewUserVariable(key string) (*UserVariable, error) {
|
||||
name := key[len("var."):]
|
||||
elem := ""
|
||||
|
|
|
@ -73,6 +73,8 @@ func (i *Interpolater) Values(
|
|||
err = i.valueResourceVar(scope, n, v, result)
|
||||
case *config.SelfVariable:
|
||||
err = i.valueSelfVar(scope, n, v, result)
|
||||
case *config.SimpleVariable:
|
||||
err = i.valueSimpleVar(scope, n, v, result)
|
||||
case *config.UserVariable:
|
||||
err = i.valueUserVar(scope, n, v, result)
|
||||
default:
|
||||
|
@ -249,6 +251,19 @@ func (i *Interpolater) valueSelfVar(
|
|||
return i.valueResourceVar(scope, n, rv, result)
|
||||
}
|
||||
|
||||
func (i *Interpolater) valueSimpleVar(
|
||||
scope *InterpolationScope,
|
||||
n string,
|
||||
v *config.SimpleVariable,
|
||||
result map[string]ast.Variable) error {
|
||||
// SimpleVars are never handled by Terraform's interpolator
|
||||
result[n] = ast.Variable{
|
||||
Value: config.UnknownVariableValue,
|
||||
Type: ast.TypeString,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Interpolater) valueUserVar(
|
||||
scope *InterpolationScope,
|
||||
n string,
|
||||
|
|
|
@ -14,7 +14,7 @@ Renders a template from a file.
|
|||
|
||||
```
|
||||
resource "template_file" "init" {
|
||||
filename = "${path.module}/init.tpl"
|
||||
template = "${file(${path.module}/init.tpl)}"
|
||||
|
||||
vars {
|
||||
consul_address = "${aws_instance.consul.private_ip}"
|
||||
|
@ -27,17 +27,24 @@ resource "template_file" "init" {
|
|||
|
||||
The following arguments are supported:
|
||||
|
||||
* `filename` - (Required) The filename for the template. Use [path
|
||||
variables](/docs/configuration/interpolation.html#path-variables) to make
|
||||
this path relative to different path roots.
|
||||
* `template` - (Required) The contents of the template. These can be loaded
|
||||
from a file on disk using the [`file()` interpolation
|
||||
function](/docs/configuration/interpolation.html#file_path_).
|
||||
|
||||
* `vars` - (Optional) Variables for interpolation within the template.
|
||||
|
||||
The following arguments are maintained for backwards compatibility and may be
|
||||
removed in a future version:
|
||||
|
||||
* `filename` - __Deprecated, please use `template` instead_. The filename for
|
||||
the template. Use [path variables](/docs/configuration/interpolation.html#path-variables) to make
|
||||
this path relative to different path roots.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `filename` - See Argument Reference above.
|
||||
* `template` - See Argument Reference above.
|
||||
* `vars` - See Argument Reference above.
|
||||
* `rendered` - The final rendered template.
|
||||
|
||||
|
|
Loading…
Reference in New Issue