core: Update TestContext2Apply_multiVarComprehensive for new assumptions
This comprehensive test was covering a few different behaviors that are intentionally different for v0.12: - Applying the splat operator to a list of resource instances that haven't been created yet produces a list of unknown values rather than a single unknown list as before. This is important because it allows that list to be passed into length(). - Wrapping a splat expression in another round of brackets now produces a list of lists, whereas before we had a special case (for compatibility with prior to v0.10) that would flatten this away in the schema layer.
This commit is contained in:
parent
cdce0d7e27
commit
3e64311dc2
|
@ -14,6 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
|
"github.com/go-test/deep"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
@ -3514,8 +3515,8 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) {
|
||||||
"source_names": {Type: cty.List(cty.String), Optional: true},
|
"source_names": {Type: cty.List(cty.String), Optional: true},
|
||||||
"source_ids_from_func": {Type: cty.List(cty.String), Optional: true},
|
"source_ids_from_func": {Type: cty.List(cty.String), Optional: true},
|
||||||
"source_names_from_func": {Type: cty.List(cty.String), Optional: true},
|
"source_names_from_func": {Type: cty.List(cty.String), Optional: true},
|
||||||
"source_ids_wrapped": {Type: cty.List(cty.String), Optional: true},
|
"source_ids_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true},
|
||||||
"source_names_wrapped": {Type: cty.List(cty.String), Optional: true},
|
"source_names_wrapped": {Type: cty.List(cty.List(cty.String)), Optional: true},
|
||||||
|
|
||||||
"id": {Type: cty.String, Computed: true},
|
"id": {Type: cty.String, Computed: true},
|
||||||
"name": {Type: cty.String, Computed: true},
|
"name": {Type: cty.String, Computed: true},
|
||||||
|
@ -3546,32 +3547,45 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) {
|
||||||
|
|
||||||
checkConfig := func(name string, want map[string]interface{}) {
|
checkConfig := func(name string, want map[string]interface{}) {
|
||||||
got := configs[name].Config
|
got := configs[name].Config
|
||||||
if !reflect.DeepEqual(got, want) {
|
t.Run("config for "+name, func(t *testing.T) {
|
||||||
t.Errorf(
|
for _, problem := range deep.Equal(got, want) {
|
||||||
"wrong config for %s\ngot: %s\nwant: %s",
|
t.Errorf(problem)
|
||||||
name, spew.Sdump(got), spew.Sdump(want),
|
}
|
||||||
)
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkConfig("test_thing.multi_count_var.0", map[string]interface{}{
|
checkConfig("test_thing.multi_count_var.0", map[string]interface{}{
|
||||||
|
"id": unknownValue(),
|
||||||
|
"name": unknownValue(),
|
||||||
"source_id": unknownValue(),
|
"source_id": unknownValue(),
|
||||||
"source_name": "test_thing.source.0",
|
"source_name": "test_thing.source.0",
|
||||||
})
|
})
|
||||||
checkConfig("test_thing.multi_count_var.2", map[string]interface{}{
|
checkConfig("test_thing.multi_count_var.2", map[string]interface{}{
|
||||||
|
"id": unknownValue(),
|
||||||
|
"name": unknownValue(),
|
||||||
"source_id": unknownValue(),
|
"source_id": unknownValue(),
|
||||||
"source_name": "test_thing.source.2",
|
"source_name": "test_thing.source.2",
|
||||||
})
|
})
|
||||||
checkConfig("test_thing.multi_count_derived.0", map[string]interface{}{
|
checkConfig("test_thing.multi_count_derived.0", map[string]interface{}{
|
||||||
|
"id": unknownValue(),
|
||||||
|
"name": unknownValue(),
|
||||||
"source_id": unknownValue(),
|
"source_id": unknownValue(),
|
||||||
"source_name": "test_thing.source.0",
|
"source_name": "test_thing.source.0",
|
||||||
})
|
})
|
||||||
checkConfig("test_thing.multi_count_derived.2", map[string]interface{}{
|
checkConfig("test_thing.multi_count_derived.2", map[string]interface{}{
|
||||||
|
"id": unknownValue(),
|
||||||
|
"name": unknownValue(),
|
||||||
"source_id": unknownValue(),
|
"source_id": unknownValue(),
|
||||||
"source_name": "test_thing.source.2",
|
"source_name": "test_thing.source.2",
|
||||||
})
|
})
|
||||||
checkConfig("test_thing.whole_splat", map[string]interface{}{
|
checkConfig("test_thing.whole_splat", map[string]interface{}{
|
||||||
"source_ids": unknownValue(),
|
"id": unknownValue(),
|
||||||
|
"name": unknownValue(),
|
||||||
|
"source_ids": []interface{}{
|
||||||
|
unknownValue(),
|
||||||
|
unknownValue(),
|
||||||
|
unknownValue(),
|
||||||
|
},
|
||||||
"source_names": []interface{}{
|
"source_names": []interface{}{
|
||||||
"test_thing.source.0",
|
"test_thing.source.0",
|
||||||
"test_thing.source.1",
|
"test_thing.source.1",
|
||||||
|
@ -3584,47 +3598,60 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) {
|
||||||
"test_thing.source.2",
|
"test_thing.source.2",
|
||||||
},
|
},
|
||||||
|
|
||||||
// This one ends up being a list with a single unknown value at this
|
"source_ids_wrapped": []interface{}{
|
||||||
// layer, but is fixed up inside helper/schema. There is a test for
|
[]interface{}{
|
||||||
// this inside the "test" provider, since core tests can't exercise
|
unknownValue(),
|
||||||
// helper/schema functionality.
|
unknownValue(),
|
||||||
"source_ids_wrapped": []interface{}{unknownValue()},
|
unknownValue(),
|
||||||
|
},
|
||||||
"source_names_wrapped": []interface{}{
|
|
||||||
"test_thing.source.0",
|
|
||||||
"test_thing.source.1",
|
|
||||||
"test_thing.source.2",
|
|
||||||
},
|
},
|
||||||
|
"source_names_wrapped": []interface{}{
|
||||||
|
[]interface{}{
|
||||||
|
"test_thing.source.0",
|
||||||
|
"test_thing.source.1",
|
||||||
|
"test_thing.source.2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
"first_source_id": unknownValue(),
|
"first_source_id": unknownValue(),
|
||||||
"first_source_name": "test_thing.source.0",
|
"first_source_name": "test_thing.source.0",
|
||||||
})
|
})
|
||||||
checkConfig("module.child.test_thing.whole_splat", map[string]interface{}{
|
checkConfig("module.child.test_thing.whole_splat", map[string]interface{}{
|
||||||
"source_ids": unknownValue(),
|
"id": unknownValue(),
|
||||||
|
"name": unknownValue(),
|
||||||
|
"source_ids": []interface{}{
|
||||||
|
unknownValue(),
|
||||||
|
unknownValue(),
|
||||||
|
unknownValue(),
|
||||||
|
},
|
||||||
"source_names": []interface{}{
|
"source_names": []interface{}{
|
||||||
"test_thing.source.0",
|
"test_thing.source.0",
|
||||||
"test_thing.source.1",
|
"test_thing.source.1",
|
||||||
"test_thing.source.2",
|
"test_thing.source.2",
|
||||||
},
|
},
|
||||||
|
|
||||||
// This one ends up being a list with a single unknown value at this
|
"source_ids_wrapped": []interface{}{
|
||||||
// layer, but is fixed up inside helper/schema. There is a test for
|
[]interface{}{
|
||||||
// this inside the "test" provider, since core tests can't exercise
|
unknownValue(),
|
||||||
// helper/schema functionality.
|
unknownValue(),
|
||||||
"source_ids_wrapped": []interface{}{unknownValue()},
|
unknownValue(),
|
||||||
|
},
|
||||||
|
},
|
||||||
"source_names_wrapped": []interface{}{
|
"source_names_wrapped": []interface{}{
|
||||||
"test_thing.source.0",
|
[]interface{}{
|
||||||
"test_thing.source.1",
|
"test_thing.source.0",
|
||||||
"test_thing.source.2",
|
"test_thing.source.1",
|
||||||
|
"test_thing.source.2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
state, diags := ctx.Apply()
|
t.Run("apply", func(t *testing.T) {
|
||||||
if diags.HasErrors() {
|
state, diags := ctx.Apply()
|
||||||
t.Fatalf("error during apply: %s", diags.Err())
|
if diags.HasErrors() {
|
||||||
}
|
t.Fatalf("error during apply: %s", diags.Err())
|
||||||
|
}
|
||||||
|
|
||||||
{
|
|
||||||
want := map[string]interface{}{
|
want := map[string]interface{}{
|
||||||
"source_ids": []interface{}{"foo", "foo", "foo"},
|
"source_ids": []interface{}{"foo", "foo", "foo"},
|
||||||
"source_names": []interface{}{
|
"source_names": []interface{}{
|
||||||
|
@ -3643,7 +3670,7 @@ func TestContext2Apply_multiVarComprehensive(t *testing.T) {
|
||||||
spew.Sdump(got), spew.Sdump(want),
|
spew.Sdump(got), spew.Sdump(want),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that multi-var (splat) access is ordered by count, not by
|
// Test that multi-var (splat) access is ordered by count, not by
|
||||||
|
|
|
@ -10,16 +10,16 @@ variable "source_names" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "test_thing" "multi_count_var" {
|
resource "test_thing" "multi_count_var" {
|
||||||
count = "${var.num}"
|
count = var.num
|
||||||
|
|
||||||
# Can pluck a single item out of a multi-var
|
# Can pluck a single item out of a multi-var
|
||||||
source_id = "${var.source_ids[count.index]}"
|
source_id = var.source_ids[count.index]
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "test_thing" "whole_splat" {
|
resource "test_thing" "whole_splat" {
|
||||||
# Can "splat" the ids directly into an attribute of type list.
|
# Can "splat" the ids directly into an attribute of type list.
|
||||||
source_ids = "${var.source_ids}"
|
source_ids = var.source_ids
|
||||||
source_names = "${var.source_names}"
|
source_names = var.source_names
|
||||||
source_ids_wrapped = ["${var.source_ids}"]
|
source_ids_wrapped = ["${var.source_ids}"]
|
||||||
source_names_wrapped = ["${var.source_names}"]
|
source_names_wrapped = ["${var.source_names}"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,48 +2,48 @@ variable "num" {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "test_thing" "source" {
|
resource "test_thing" "source" {
|
||||||
count = "${var.num}"
|
count = var.num
|
||||||
|
|
||||||
# The diffFunc in the test exports "name" here too, which we can use
|
# The diffFunc in the test exports "name" here too, which we can use
|
||||||
# to test values that are known during plan.
|
# to test values that are known during plan.
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "test_thing" "multi_count_var" {
|
resource "test_thing" "multi_count_var" {
|
||||||
count = "${var.num}"
|
count = var.num
|
||||||
|
|
||||||
# Can pluck a single item out of a multi-var
|
# Can pluck a single item out of a multi-var
|
||||||
source_id = "${test_thing.source.*.id[count.index]}"
|
source_id = test_thing.source.*.id[count.index]
|
||||||
source_name = "${test_thing.source.*.name[count.index]}"
|
source_name = test_thing.source.*.name[count.index]
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "test_thing" "multi_count_derived" {
|
resource "test_thing" "multi_count_derived" {
|
||||||
# Can use the source to get the count
|
# Can use the source to get the count
|
||||||
count = "${length(test_thing.source)}"
|
count = length(test_thing.source)
|
||||||
|
|
||||||
source_id = "${test_thing.source.*.id[count.index]}"
|
source_id = test_thing.source.*.id[count.index]
|
||||||
source_name = "${test_thing.source.*.name[count.index]}"
|
source_name = test_thing.source.*.name[count.index]
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "test_thing" "whole_splat" {
|
resource "test_thing" "whole_splat" {
|
||||||
# Can "splat" the ids directly into an attribute of type list.
|
# Can "splat" the ids directly into an attribute of type list.
|
||||||
source_ids = "${test_thing.source.*.id}"
|
source_ids = test_thing.source.*.id
|
||||||
source_names = "${test_thing.source.*.name}"
|
source_names = test_thing.source.*.name
|
||||||
|
|
||||||
# Accessing through a function should work.
|
# Accessing through a function should work.
|
||||||
source_ids_from_func = "${split(" ", join(" ", test_thing.source.*.id))}"
|
source_ids_from_func = split(" ", join(" ", test_thing.source.*.id))
|
||||||
source_names_from_func = "${split(" ", join(" ", test_thing.source.*.name))}"
|
source_names_from_func = split(" ", join(" ", test_thing.source.*.name))
|
||||||
|
|
||||||
# A common pattern of selecting with a default.
|
# A common pattern of selecting with a default.
|
||||||
first_source_id = "${element(concat(test_thing.source.*.id, list("default")), 0)}"
|
first_source_id = element(concat(test_thing.source.*.id, ["default"]), 0)
|
||||||
first_source_name = "${element(concat(test_thing.source.*.name, list("default")), 0)}"
|
first_source_name = element(concat(test_thing.source.*.name, ["default"]), 0)
|
||||||
|
|
||||||
# Legacy form: Prior to Terraform having comprehensive list support,
|
# Prior to v0.12 we were handling lists containing list interpolations as
|
||||||
# splats were treated as a special case and required to be presented
|
# a special case, flattening the result, for compatibility with behavior
|
||||||
# in a wrapping list. This is no longer the suggested form, but we
|
# prior to v0.10. This deprecated handling is now removed, and so these
|
||||||
# need it to keep working for compatibility.
|
# each produce a list of lists. We're still using the interpolation syntax
|
||||||
#
|
# here, rather than the splat expression directly, to properly mimic how
|
||||||
# This should result in exactly the same result as the above, even
|
# this would've looked prior to v0.12 to be explicit about what the new
|
||||||
# though it looks like it would result in a list of lists.
|
# behavior is for this old syntax.
|
||||||
source_ids_wrapped = ["${test_thing.source.*.id}"]
|
source_ids_wrapped = ["${test_thing.source.*.id}"]
|
||||||
source_names_wrapped = ["${test_thing.source.*.name}"]
|
source_names_wrapped = ["${test_thing.source.*.name}"]
|
||||||
|
|
||||||
|
@ -52,15 +52,15 @@ resource "test_thing" "whole_splat" {
|
||||||
module "child" {
|
module "child" {
|
||||||
source = "./child"
|
source = "./child"
|
||||||
|
|
||||||
num = "${var.num}"
|
num = var.num
|
||||||
source_ids = "${test_thing.source.*.id}"
|
source_ids = test_thing.source.*.id
|
||||||
source_names = "${test_thing.source.*.name}"
|
source_names = test_thing.source.*.name
|
||||||
}
|
}
|
||||||
|
|
||||||
output "source_ids" {
|
output "source_ids" {
|
||||||
value = "${test_thing.source.*.id}"
|
value = test_thing.source.*.id
|
||||||
}
|
}
|
||||||
|
|
||||||
output "source_names" {
|
output "source_names" {
|
||||||
value = "${test_thing.source.*.name}"
|
value = test_thing.source.*.name
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue