From dd43926761a31932eff31b6a637e975fdd810e21 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Fri, 22 Feb 2019 15:55:26 -0800 Subject: [PATCH] configs/configupgrade: Fix up uses of the .count pseudo-attribute Terraform 0.11 and prior had an odd special case where a resource attribute access for "count" would be resolved as the count for the whole resource, rather than as an attribute of an individual instance as for all other attributes. Because Terraform 0.12 makes test_instance.foo appear as a list when count is set (so it can be used in other expressions), it's no longer possible to have an attribute in that position: lists don't have attributes. Fortunately we don't really need that special case anymore since it doesn't do anything we can't now do with the length(...) function. This upgrade rule, then, detects references like test_instance.foo.count and rewrites to length(test_instance.foo). As a special case, if test_instance.foo doesn't have "count" set then it just rewrites as the constant 1, which mimics what would've happened in that case in Terraform 0.11. --- .../input/resource-count-ref.tf | 29 ++++++++++++++ .../want/resource-count-ref.tf | 29 ++++++++++++++ .../valid/resource-count-ref/want/versions.tf | 3 ++ configs/configupgrade/upgrade_expr.go | 38 +++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 configs/configupgrade/test-fixtures/valid/resource-count-ref/input/resource-count-ref.tf create mode 100644 configs/configupgrade/test-fixtures/valid/resource-count-ref/want/resource-count-ref.tf create mode 100644 configs/configupgrade/test-fixtures/valid/resource-count-ref/want/versions.tf diff --git a/configs/configupgrade/test-fixtures/valid/resource-count-ref/input/resource-count-ref.tf b/configs/configupgrade/test-fixtures/valid/resource-count-ref/input/resource-count-ref.tf new file mode 100644 index 000000000..6b4647c3b --- /dev/null +++ b/configs/configupgrade/test-fixtures/valid/resource-count-ref/input/resource-count-ref.tf @@ -0,0 +1,29 @@ +resource "test_instance" "one" { +} + +resource "test_instance" "many" { + count = 2 +} + +data "terraform_remote_state" "one" { +} + +data "terraform_remote_state" "many" { + count = 2 +} + +output "managed_one" { + value = "${test_instance.one.count}" +} + +output "managed_many" { + value = "${test_instance.many.count}" +} + +output "data_one" { + value = "${data.terraform_remote_state.one.count}" +} + +output "data_many" { + value = "${data.terraform_remote_state.many.count}" +} diff --git a/configs/configupgrade/test-fixtures/valid/resource-count-ref/want/resource-count-ref.tf b/configs/configupgrade/test-fixtures/valid/resource-count-ref/want/resource-count-ref.tf new file mode 100644 index 000000000..a20b45d88 --- /dev/null +++ b/configs/configupgrade/test-fixtures/valid/resource-count-ref/want/resource-count-ref.tf @@ -0,0 +1,29 @@ +resource "test_instance" "one" { +} + +resource "test_instance" "many" { + count = 2 +} + +data "terraform_remote_state" "one" { +} + +data "terraform_remote_state" "many" { + count = 2 +} + +output "managed_one" { + value = 1 +} + +output "managed_many" { + value = length(test_instance.many) +} + +output "data_one" { + value = 1 +} + +output "data_many" { + value = length(data.terraform_remote_state.many) +} diff --git a/configs/configupgrade/test-fixtures/valid/resource-count-ref/want/versions.tf b/configs/configupgrade/test-fixtures/valid/resource-count-ref/want/versions.tf new file mode 100644 index 000000000..d9b6f790b --- /dev/null +++ b/configs/configupgrade/test-fixtures/valid/resource-count-ref/want/versions.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">= 0.12" +} diff --git a/configs/configupgrade/upgrade_expr.go b/configs/configupgrade/upgrade_expr.go index de4480af4..8ff8bb51c 100644 --- a/configs/configupgrade/upgrade_expr.go +++ b/configs/configupgrade/upgrade_expr.go @@ -224,6 +224,44 @@ Value: // here so we can normalize and introduce some newer syntax where it's // safe to do so. parts := strings.Split(tv.Name, ".") + + // First we need to deal with the .count pseudo-attributes that 0.11 and + // prior allowed for resources. These no longer exist, because they + // don't do anything we can't do with the length(...) function. + if len(parts) > 0 { + var rAddr addrs.Resource + switch parts[0] { + case "data": + if len(parts) == 4 && parts[3] == "count" { + rAddr.Mode = addrs.DataResourceMode + rAddr.Type = parts[1] + rAddr.Name = parts[2] + } + default: + if len(parts) == 3 && parts[2] == "count" { + rAddr.Mode = addrs.ManagedResourceMode + rAddr.Type = parts[0] + rAddr.Name = parts[1] + } + } + + // We need to check if the thing being referenced is actually an + // existing resource, because other three-part traversals might + // coincidentally end with "count". + if hasCount, exists := an.ResourceHasCount[rAddr]; exists { + if hasCount { + buf.WriteString("length(") + buf.WriteString(rAddr.String()) + buf.WriteString(")") + } else { + // If the resource does not have count, the .count + // attr would've always returned 1 before. + buf.WriteString("1") + } + break Value + } + } + parts = upgradeTraversalParts(parts, an) // might add/remove/change parts first, remain := parts[0], parts[1:] buf.WriteString(first)