Merge pull request #2386 from hashicorp/b-template-variable-change-failure
provider/template: don't error when rendering fails in Exists
This commit is contained in:
commit
5e86e709bc
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
@ -75,8 +76,14 @@ func Delete(d *schema.ResourceData, meta interface{}) error {
|
||||||
func Exists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
func Exists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
||||||
rendered, err := render(d)
|
rendered, err := render(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if _, ok := err.(templateRenderError); ok {
|
||||||
|
log.Printf("[DEBUG] Got error while rendering in Exists: %s", err)
|
||||||
|
log.Printf("[DEBUG] Returning false so the template re-renders using latest variables from config.")
|
||||||
|
return false, nil
|
||||||
|
} else {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return hash(rendered) == d.Id(), nil
|
return hash(rendered) == d.Id(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +94,8 @@ func Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type templateRenderError error
|
||||||
|
|
||||||
var readfile func(string) ([]byte, error) = ioutil.ReadFile // testing hook
|
var readfile func(string) ([]byte, error) = ioutil.ReadFile // testing hook
|
||||||
|
|
||||||
func render(d *schema.ResourceData) (string, error) {
|
func render(d *schema.ResourceData) (string, error) {
|
||||||
|
@ -105,7 +114,9 @@ func render(d *schema.ResourceData) (string, error) {
|
||||||
|
|
||||||
rendered, err := execute(string(buf), vars)
|
rendered, err := execute(string(buf), vars)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("failed to render %v: %v", filename, err)
|
return "", templateRenderError(
|
||||||
|
fmt.Errorf("failed to render %v: %v", filename, err),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rendered, nil
|
return rendered, nil
|
||||||
|
|
|
@ -34,15 +34,7 @@ func TestTemplateRendering(t *testing.T) {
|
||||||
Providers: testProviders,
|
Providers: testProviders,
|
||||||
Steps: []r.TestStep{
|
Steps: []r.TestStep{
|
||||||
r.TestStep{
|
r.TestStep{
|
||||||
Config: `
|
Config: testTemplateConfig(tt.vars),
|
||||||
resource "template_file" "t0" {
|
|
||||||
filename = "mock"
|
|
||||||
vars = ` + tt.vars + `
|
|
||||||
}
|
|
||||||
output "rendered" {
|
|
||||||
value = "${template_file.t0.rendered}"
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
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 {
|
||||||
|
@ -55,3 +47,55 @@ output "rendered" {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/hashicorp/terraform/issues/2344
|
||||||
|
func TestTemplateVariableChange(t *testing.T) {
|
||||||
|
steps := []struct {
|
||||||
|
vars string
|
||||||
|
template string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{`{a="foo"}`, `${a}`, `foo`},
|
||||||
|
{`{b="bar"}`, `${b}`, `bar`},
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
Check: func(i int, want string) r.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
got := s.RootModule().Outputs["rendered"]
|
||||||
|
if want != got {
|
||||||
|
return fmt.Errorf("[%d] got:\n%q\nwant:\n%q\n", i, got, want)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}(i, step.want),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Test(t, r.TestCase{
|
||||||
|
Providers: testProviders,
|
||||||
|
Steps: testSteps,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTemplateConfig(vars string) string {
|
||||||
|
return `
|
||||||
|
resource "template_file" "t0" {
|
||||||
|
filename = "mock"
|
||||||
|
vars = ` + vars + `
|
||||||
|
}
|
||||||
|
output "rendered" {
|
||||||
|
value = "${template_file.t0.rendered}"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
|
@ -60,6 +60,10 @@ type TestCase struct {
|
||||||
// potentially complex update logic. In general, simply create/destroy
|
// potentially complex update logic. In general, simply create/destroy
|
||||||
// tests will only need one step.
|
// tests will only need one step.
|
||||||
type TestStep struct {
|
type TestStep struct {
|
||||||
|
// PreConfig is called before the Config is applied to perform any per-step
|
||||||
|
// setup that needs to happen
|
||||||
|
PreConfig func()
|
||||||
|
|
||||||
// Config a string of the configuration to give to Terraform.
|
// Config a string of the configuration to give to Terraform.
|
||||||
Config string
|
Config string
|
||||||
|
|
||||||
|
@ -160,6 +164,10 @@ func testStep(
|
||||||
opts terraform.ContextOpts,
|
opts terraform.ContextOpts,
|
||||||
state *terraform.State,
|
state *terraform.State,
|
||||||
step TestStep) (*terraform.State, error) {
|
step TestStep) (*terraform.State, error) {
|
||||||
|
if step.PreConfig != nil {
|
||||||
|
step.PreConfig()
|
||||||
|
}
|
||||||
|
|
||||||
cfgPath, err := ioutil.TempDir("", "tf-test")
|
cfgPath, err := ioutil.TempDir("", "tf-test")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state, fmt.Errorf(
|
return state, fmt.Errorf(
|
||||||
|
|
Loading…
Reference in New Issue