From cabcc4b5b8aba01d623fd789eed599bcdae38000 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 10 Dec 2016 19:17:29 -0500 Subject: [PATCH] terraform: allow indexing into a computed list for multi-count resources Fixes #8695 When a list count was computed in a multi-resource access (foo.bar.*.list), we were returning the value as empty string. I don't actually know the histocal reasoning for this but this can't be correct: we must return unknown. When changing this to unknown, the new tests passed and none of the old tests failed. This leads me further to believe that the return empty string is probably a holdover from long ago to just avoid crashes or UUIDs in the plan output and not actually the correct behavior. --- terraform/context_plan_test.go | 25 ++++++++++ terraform/interpolate.go | 2 +- terraform/interpolate_test.go | 49 +++++++++++++++++++ terraform/terraform_test.go | 18 +++++++ .../plan-computed-multi-index/main.tf | 9 ++++ 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 terraform/test-fixtures/plan-computed-multi-index/main.tf diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index aafb74b0764a..9dd6e14f4da8 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -1319,6 +1319,31 @@ func TestContext2Plan_computedList(t *testing.T) { } } +// GH-8695. This tests that you can index into a computed list on a +// splatted resource. +func TestContext2Plan_computedMultiIndex(t *testing.T) { + m := testModule(t, "plan-computed-multi-index") + p := testProvider("aws") + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + Providers: map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + }) + + plan, err := ctx.Plan() + if err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(plan.String()) + expected := strings.TrimSpace(testTerraformPlanComputedMultiIndexStr) + if actual != expected { + t.Fatalf("bad:\n%s", actual) + } +} + func TestContext2Plan_count(t *testing.T) { m := testModule(t, "plan-count") p := testProvider("aws") diff --git a/terraform/interpolate.go b/terraform/interpolate.go index 3d88da844542..252fb7637394 100644 --- a/terraform/interpolate.go +++ b/terraform/interpolate.go @@ -587,7 +587,7 @@ func (i *Interpolater) computeResourceMultiVariable( } if multiAttr == unknownVariable { - return &ast.Variable{Type: ast.TypeString, Value: ""}, nil + return &unknownVariable, nil } values = append(values, multiAttr) diff --git a/terraform/interpolate_test.go b/terraform/interpolate_test.go index 4eaf882bc7ca..9613fc776d75 100644 --- a/terraform/interpolate_test.go +++ b/terraform/interpolate_test.go @@ -364,6 +364,55 @@ func TestInterpolater_resourceVariableMulti(t *testing.T) { }) } +// When a splat reference is made to an attribute that is a computed list, +// the result should be unknown. +func TestInterpolater_resourceVariableMultiList(t *testing.T) { + lock := new(sync.RWMutex) + state := &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "aws_instance.web.0": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "ip.#": config.UnknownVariableValue, + }, + }, + }, + + "aws_instance.web.1": &ResourceState{ + Type: "aws_instance", + Primary: &InstanceState{ + ID: "bar", + Attributes: map[string]string{ + "ip.#": "0", + }, + }, + }, + }, + }, + }, + } + + i := &Interpolater{ + Module: testModule(t, "interpolate-resource-variable"), + State: state, + StateLock: lock, + } + + scope := &InterpolationScope{ + Path: rootModulePath, + } + + testInterpolate(t, i, scope, "aws_instance.web.*.ip", ast.Variable{ + Value: config.UnknownVariableValue, + Type: ast.TypeUnknown, + }) +} + func TestInterpolater_resourceVariableMulti_interpolated(t *testing.T) { lock := new(sync.RWMutex) state := &State{ diff --git a/terraform/terraform_test.go b/terraform/terraform_test.go index 40e4935acbf7..023da95b170c 100644 --- a/terraform/terraform_test.go +++ b/terraform/terraform_test.go @@ -943,6 +943,24 @@ STATE: ` +const testTerraformPlanComputedMultiIndexStr = ` +DIFF: + +CREATE: aws_instance.bar + foo: "" => "" + type: "" => "aws_instance" +CREATE: aws_instance.foo.0 + ip.#: "" => "" + type: "" => "aws_instance" +CREATE: aws_instance.foo.1 + ip.#: "" => "" + type: "" => "aws_instance" + +STATE: + + +` + const testTerraformPlanCountStr = ` DIFF: diff --git a/terraform/test-fixtures/plan-computed-multi-index/main.tf b/terraform/test-fixtures/plan-computed-multi-index/main.tf new file mode 100644 index 000000000000..2d8a799d0587 --- /dev/null +++ b/terraform/test-fixtures/plan-computed-multi-index/main.tf @@ -0,0 +1,9 @@ +resource "aws_instance" "foo" { + count = 2 + compute = "ip.#" +} + +resource "aws_instance" "bar" { + count = 1 + foo = "${aws_instance.foo.*.ip[count.index]}" +}