diff --git a/config/config.go b/config/config.go index c085e1f26..06688d31d 100644 --- a/config/config.go +++ b/config/config.go @@ -84,8 +84,8 @@ type Resource struct { // ResourceLifecycle is used to store the lifecycle tuning parameters // to allow customized behavior type ResourceLifecycle struct { - CreateBeforeDestroy bool `hcl:"create_before_destroy"` - PreventDestroy bool `hcl:"prevent_destroy"` + CreateBeforeDestroy bool `mapstructure:"create_before_destroy"` + PreventDestroy bool `mapstructure:"prevent_destroy"` } // Provisioner is a configured provisioner step on a resource. diff --git a/config/loader_hcl.go b/config/loader_hcl.go index 6f4fa8edd..b20900bfb 100644 --- a/config/loader_hcl.go +++ b/config/loader_hcl.go @@ -6,6 +6,7 @@ import ( "github.com/hashicorp/hcl" hclobj "github.com/hashicorp/hcl/hcl" + "github.com/mitchellh/mapstructure" ) // hclConfigurable is an implementation of configurable that knows @@ -521,8 +522,16 @@ func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) { // destroying the existing instance var lifecycle ResourceLifecycle if o := obj.Get("lifecycle", false); o != nil { - err = hcl.DecodeObject(&lifecycle, o) - if err != nil { + var raw map[string]interface{} + if err = hcl.DecodeObject(&raw, o); err != nil { + return nil, fmt.Errorf( + "Error parsing lifecycle for %s[%s]: %s", + t.Key, + k, + err) + } + + if err := mapstructure.WeakDecode(raw, &lifecycle); err != nil { return nil, fmt.Errorf( "Error parsing lifecycle for %s[%s]: %s", t.Key, diff --git a/config/loader_test.go b/config/loader_test.go index 28ee242f5..53960a580 100644 --- a/config/loader_test.go +++ b/config/loader_test.go @@ -440,7 +440,44 @@ func TestLoadFile_createBeforeDestroy(t *testing.T) { } } -func TestLoadDir_temporary_files(t *testing.T) { +func TestLoad_preventDestroyString(t *testing.T) { + c, err := Load(filepath.Join(fixtureDir, "prevent-destroy-string.tf")) + if err != nil { + t.Fatalf("err: %s", err) + } + + if c == nil { + t.Fatal("config should not be nil") + } + + actual := resourcesStr(c.Resources) + if actual != strings.TrimSpace(createBeforeDestroyResourcesStr) { + t.Fatalf("bad:\n%s", actual) + } + + // Check for the flag value + r := c.Resources[0] + if r.Name != "web" && r.Type != "aws_instance" { + t.Fatalf("Bad: %#v", r) + } + + // Should enable create before destroy + if !r.Lifecycle.PreventDestroy { + t.Fatalf("Bad: %#v", r) + } + + r = c.Resources[1] + if r.Name != "bar" && r.Type != "aws_instance" { + t.Fatalf("Bad: %#v", r) + } + + // Should not enable create before destroy + if r.Lifecycle.PreventDestroy { + t.Fatalf("Bad: %#v", r) + } +} + +func TestLoad_temporary_files(t *testing.T) { _, err := LoadDir(filepath.Join(fixtureDir, "dir-temporary-files")) if err == nil { t.Fatalf("Expected to see an error stating no config files found") diff --git a/config/test-fixtures/prevent-destroy-string.tf b/config/test-fixtures/prevent-destroy-string.tf new file mode 100644 index 000000000..c5e253a14 --- /dev/null +++ b/config/test-fixtures/prevent-destroy-string.tf @@ -0,0 +1,10 @@ +resource "aws_instance" "web" { + ami = "foo" + lifecycle { + prevent_destroy = "true" + } +} + +resource "aws_instance" "bar" { + ami = "foo" +}