Merge pull request #3909 from hashicorp/phinze/template-file-contents

template_file: source contents instead of path
This commit is contained in:
Paul Hinze 2015-11-16 14:50:45 -06:00
commit 993ec0a320
5 changed files with 80 additions and 41 deletions

View File

@ -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),

View File

@ -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)
}

View File

@ -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 := ""

View File

@ -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,

View File

@ -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.