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