From 79b948c9cc3b281b54302d1fbf7490cc25104553 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 3 Apr 2018 10:00:45 -0400 Subject: [PATCH] detect scaled in resources when evaluating *s If an existing resources is scaled back to 0, locals and outputs will still have a multi-variable reference to evaluate, which should return an empty list. Due to how the resource is removed, the resource will still exist in the state but with no primary instance, which needs to be ignored in the instance count. --- terraform/context_apply_test.go | 59 +++++++++++++++++++ terraform/interpolate.go | 8 ++- .../apply-resource-scale-in/main.tf | 13 ++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 terraform/test-fixtures/apply-resource-scale-in/main.tf diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index f53bae1d9..184ee540b 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -9541,3 +9541,62 @@ func TestContext2Apply_plannedInterpolatedCount(t *testing.T) { t.Fatalf("apply failed: %s", err) } } + +func TestContext2Apply_scaleInMultivarRef(t *testing.T) { + m := testModule(t, "apply-resource-scale-in") + + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + + providerResolver := ResourceProviderResolverFixed( + map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + ) + + s := &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: rootModulePath, + Resources: map[string]*ResourceState{ + "aws_instance.one": { + Type: "aws_instance", + Primary: &InstanceState{ + ID: "foo", + }, + Provider: "provider.aws", + }, + "aws_instance.two": { + Type: "aws_instance", + Primary: &InstanceState{ + ID: "foo", + Attributes: map[string]string{ + "val": "foo", + }, + }, + Provider: "provider.aws", + }, + }, + }, + }, + } + + ctx := testContext2(t, &ContextOpts{ + Module: m, + ProviderResolver: providerResolver, + State: s, + Variables: map[string]interface{}{"count": "0"}, + }) + + _, err := ctx.Plan() + if err != nil { + t.Fatalf("plan failed: %s", err) + } + + // Applying the plan should now succeed + _, err = ctx.Apply() + if err != nil { + t.Fatalf("apply failed: %s", err) + } +} diff --git a/terraform/interpolate.go b/terraform/interpolate.go index 1509a65fe..f3d9adefe 100644 --- a/terraform/interpolate.go +++ b/terraform/interpolate.go @@ -820,7 +820,13 @@ func (i *Interpolater) resourceCountMax( // use "cr.Count()" but that doesn't work if the count is interpolated // and we can't guarantee that so we instead depend on the state. max := -1 - for k, _ := range ms.Resources { + for k, s := range ms.Resources { + // This resource may have been just removed, in which case the Primary + // may be nil, or just empty. + if s == nil || s.Primary == nil || len(s.Primary.Attributes) == 0 { + continue + } + // Get the index number for this resource index := "" if k == id { diff --git a/terraform/test-fixtures/apply-resource-scale-in/main.tf b/terraform/test-fixtures/apply-resource-scale-in/main.tf new file mode 100644 index 000000000..da5ac8e4d --- /dev/null +++ b/terraform/test-fixtures/apply-resource-scale-in/main.tf @@ -0,0 +1,13 @@ +variable "count" {} + +resource "aws_instance" "one" { + count = "${var.count}" +} + +locals { + "one_id" = "${element(concat(aws_instance.one.*.id, list("")), 0)}" +} + +resource "aws_instance" "two" { + val = "${local.one_id}" +}