diff --git a/terraform/state.go b/terraform/state.go index f7ba29733..930ac7e74 100644 --- a/terraform/state.go +++ b/terraform/state.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "sort" "sync" "github.com/hashicorp/terraform/config" @@ -29,7 +30,15 @@ func (s *State) init() { func (s *State) String() string { var buf bytes.Buffer - for k, rs := range s.Resources { + names := make([]string, 0, len(s.Resources)) + for name, _ := range s.Resources { + names = append(names, name) + } + sort.Strings(names) + + for _, k := range names { + rs := s.Resources[k] + buf.WriteString(fmt.Sprintf("%s:\n", k)) buf.WriteString(fmt.Sprintf(" ID = %s\n", rs.ID)) diff --git a/terraform/terraform.go b/terraform/terraform.go index 893f10078..db0627fa1 100644 --- a/terraform/terraform.go +++ b/terraform/terraform.go @@ -136,7 +136,13 @@ func (t *Terraform) applyWalkFn( result.init() cb := func(r *Resource) (map[string]string, error) { - rs, err := r.Provider.Apply(r.State, r.Diff) + // Get the latest diff since there are no computed values anymore + diff, err := r.Provider.Diff(r.State, r.Config) + if err != nil { + return nil, err + } + + rs, err := r.Provider.Apply(r.State, diff) if err != nil { return nil, err } diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index 8b6d5ae78..0e250a500 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -1,6 +1,7 @@ package terraform import ( + "fmt" "path/filepath" "strings" "testing" @@ -228,6 +229,33 @@ func TestTerraformApply(t *testing.T) { } } +func TestTerraformApply_compute(t *testing.T) { + // This tests that computed variables are properly re-diffed + // to get the value prior to application (Apply). + tf := testTerraform(t, "apply-compute") + + s := &State{} + p, err := tf.Plan(s) + if err != nil { + t.Fatalf("err: %s", err) + } + + // Set meta to change behavior so that computed variables are filled + testProviderMock(testProvider(tf, "aws_instance.foo")).Meta = + "compute" + + state, err := tf.Apply(p) + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(state.String()) + expected := strings.TrimSpace(testTerraformApplyComputeStr) + if actual != expected { + t.Fatalf("bad: \n%s", actual) + } +} + func TestTerraformPlan(t *testing.T) { tf := testTerraform(t, "plan-good") @@ -324,6 +352,8 @@ func testProviderFunc(n string, rs []string) ResourceProviderFactory { } return func() (ResourceProvider, error) { + p := &MockResourceProvider{Meta: n} + applyFn := func( s *ResourceState, d *ResourceDiff) (*ResourceState, error) { @@ -361,11 +391,20 @@ func testProviderFunc(n string, rs []string) ResourceProviderFactory { } if k == "compute" { - diff.Attributes[v.(string)] = &ResourceAttrDiff{ + attrDiff := &ResourceAttrDiff{ Old: "", New: "", NewComputed: true, } + + // If the value of Meta turns into "compute", then we + // fill the computed values. + if mv, ok := p.Meta.(string); ok && mv == "compute" { + attrDiff.NewComputed = false + attrDiff.New = fmt.Sprintf("computed_%s", v.(string)) + } + + diff.Attributes[v.(string)] = attrDiff continue } @@ -408,15 +447,12 @@ func testProviderFunc(n string, rs []string) ResourceProviderFactory { return s, nil } - result := &MockResourceProvider{ - Meta: n, - ApplyFn: applyFn, - DiffFn: diffFn, - RefreshFn: refreshFn, - ResourcesReturn: resources, - } + p.ApplyFn = applyFn + p.DiffFn = diffFn + p.RefreshFn = refreshFn + p.ResourcesReturn = resources - return result, nil + return p, nil } } @@ -502,6 +538,18 @@ aws_instance.foo: num = 2 ` +const testTerraformApplyComputeStr = ` +aws_instance.bar: + ID = foo + type = aws_instance + foo = computed_id +aws_instance.foo: + ID = foo + type = aws_instance + num = 2 + id = computed_id +` + const testTerraformPlanStr = ` UPDATE: aws_instance.bar foo: "" => "2" diff --git a/terraform/test-fixtures/apply-compute/main.tf b/terraform/test-fixtures/apply-compute/main.tf new file mode 100644 index 000000000..6f886cb62 --- /dev/null +++ b/terraform/test-fixtures/apply-compute/main.tf @@ -0,0 +1,8 @@ +resource "aws_instance" "foo" { + num = "2" + compute = "id" +} + +resource "aws_instance" "bar" { + foo = "${aws_instance.foo.id}" +}