Merge pull request #13793 from paybyphone/data_source_plan_count_boundary
core: Add CountBoundaryTransformer to the plan graph builder
This commit is contained in:
commit
78c2720a4c
|
@ -99,3 +99,59 @@ resource "test_resource" "foo" {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDataSource_dataSourceCountGrandChild tests that a grandchild data source
|
||||||
|
// that is based off of count works, ie: dependency chain foo -> bar -> baz.
|
||||||
|
// This was failing because CountBoundaryTransformer is being run during apply
|
||||||
|
// instead of plan, which meant that it wasn't firing after data sources were
|
||||||
|
// potentially changing state and causing diff/interpolation issues.
|
||||||
|
//
|
||||||
|
// This happens after the initial apply, after state is saved.
|
||||||
|
func TestDataSource_dataSourceCountGrandChild(t *testing.T) {
|
||||||
|
resource.UnitTest(t, resource.TestCase{
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: func(s *terraform.State) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
{
|
||||||
|
Config: dataSourceCountGrandChildConfig,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config: dataSourceCountGrandChildConfig,
|
||||||
|
Check: func(s *terraform.State) error {
|
||||||
|
for _, v := range []string{"foo", "bar", "baz"} {
|
||||||
|
count := 0
|
||||||
|
for k := range s.RootModule().Resources {
|
||||||
|
if strings.HasPrefix(k, fmt.Sprintf("data.test_data_source.%s.", v)) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count != 2 {
|
||||||
|
return fmt.Errorf("bad count for data.test_data_source.%s: %d", v, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataSourceCountGrandChildConfig = `
|
||||||
|
data "test_data_source" "foo" {
|
||||||
|
count = 2
|
||||||
|
input = "one"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "test_data_source" "bar" {
|
||||||
|
count = "${length(data.test_data_source.foo.*.id)}"
|
||||||
|
input = "${data.test_data_source.foo.*.output[count.index]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
data "test_data_source" "baz" {
|
||||||
|
count = "${length(data.test_data_source.bar.*.id)}"
|
||||||
|
input = "${data.test_data_source.bar.*.output[count.index]}"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
|
@ -3154,3 +3154,146 @@ func TestContext2Plan_ignoreChangesWithFlatmaps(t *testing.T) {
|
||||||
t.Fatalf("bad:\n%s\n\nexpected\n\n%s", actual, expected)
|
t.Fatalf("bad:\n%s\n\nexpected\n\n%s", actual, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestContext2Plan_resourceNestedCount ensures resource sets that depend on
|
||||||
|
// the count of another resource set (ie: count of a data source that depends
|
||||||
|
// on another data source's instance count - data.x.foo.*.id) get properly
|
||||||
|
// normalized to the indexes they should be. This case comes up when there is
|
||||||
|
// an existing state (after an initial apply).
|
||||||
|
func TestContext2Plan_resourceNestedCount(t *testing.T) {
|
||||||
|
m := testModule(t, "nested-resource-count-plan")
|
||||||
|
p := testProvider("aws")
|
||||||
|
p.DiffFn = testDiffFn
|
||||||
|
p.RefreshFn = func(i *InstanceInfo, is *InstanceState) (*InstanceState, error) {
|
||||||
|
return is, nil
|
||||||
|
}
|
||||||
|
s := &State{
|
||||||
|
Modules: []*ModuleState{
|
||||||
|
&ModuleState{
|
||||||
|
Path: rootModulePath,
|
||||||
|
Resources: map[string]*ResourceState{
|
||||||
|
"aws_instance.foo.0": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "foo0",
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"id": "foo0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"aws_instance.foo.1": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "foo1",
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"id": "foo1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"aws_instance.bar.0": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Dependencies: []string{"aws_instance.foo.*"},
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "bar0",
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"id": "bar0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"aws_instance.bar.1": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Dependencies: []string{"aws_instance.foo.*"},
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "bar1",
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"id": "bar1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"aws_instance.baz.0": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Dependencies: []string{"aws_instance.bar.*"},
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "baz0",
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"id": "baz0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"aws_instance.baz.1": &ResourceState{
|
||||||
|
Type: "aws_instance",
|
||||||
|
Dependencies: []string{"aws_instance.bar.*"},
|
||||||
|
Primary: &InstanceState{
|
||||||
|
ID: "baz1",
|
||||||
|
Attributes: map[string]string{
|
||||||
|
"id": "baz1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ctx := testContext2(t, &ContextOpts{
|
||||||
|
Module: m,
|
||||||
|
Providers: map[string]ResourceProviderFactory{
|
||||||
|
"aws": testProviderFuncFixed(p),
|
||||||
|
},
|
||||||
|
State: s,
|
||||||
|
})
|
||||||
|
|
||||||
|
w, e := ctx.Validate()
|
||||||
|
if len(w) > 0 {
|
||||||
|
t.Fatalf("warnings generated on validate: %#v", w)
|
||||||
|
}
|
||||||
|
if len(e) > 0 {
|
||||||
|
t.Fatalf("errors generated on validate: %#v", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := ctx.Refresh()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("refresh err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
plan, err := ctx.Plan()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("plan err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := strings.TrimSpace(plan.String())
|
||||||
|
expected := strings.TrimSpace(`
|
||||||
|
DIFF:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
STATE:
|
||||||
|
|
||||||
|
aws_instance.bar.0:
|
||||||
|
ID = bar0
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
aws_instance.foo.*
|
||||||
|
aws_instance.bar.1:
|
||||||
|
ID = bar1
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
aws_instance.foo.*
|
||||||
|
aws_instance.baz.0:
|
||||||
|
ID = baz0
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
aws_instance.bar.*
|
||||||
|
aws_instance.baz.1:
|
||||||
|
ID = baz1
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
aws_instance.bar.*
|
||||||
|
aws_instance.foo.0:
|
||||||
|
ID = foo0
|
||||||
|
aws_instance.foo.1:
|
||||||
|
ID = foo1
|
||||||
|
`)
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("bad:\n%s\n\nexpected\n\n%s", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -113,6 +113,9 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||||
// have to connect again later for providers and so on.
|
// have to connect again later for providers and so on.
|
||||||
&ReferenceTransformer{},
|
&ReferenceTransformer{},
|
||||||
|
|
||||||
|
// Add the node to fix the state count boundaries
|
||||||
|
&CountBoundaryTransformer{},
|
||||||
|
|
||||||
// Target
|
// Target
|
||||||
&TargetsTransformer{Targets: b.Targets},
|
&TargetsTransformer{Targets: b.Targets},
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ func TestPlanGraphBuilder(t *testing.T) {
|
||||||
actual := strings.TrimSpace(g.String())
|
actual := strings.TrimSpace(g.String())
|
||||||
expected := strings.TrimSpace(testPlanGraphBuilderStr)
|
expected := strings.TrimSpace(testPlanGraphBuilderStr)
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
t.Fatalf("bad: %s", actual)
|
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,14 @@ aws_load_balancer.weblb
|
||||||
provider.aws
|
provider.aws
|
||||||
aws_security_group.firewall
|
aws_security_group.firewall
|
||||||
provider.aws
|
provider.aws
|
||||||
|
meta.count-boundary (count boundary fixup)
|
||||||
|
aws_instance.web
|
||||||
|
aws_load_balancer.weblb
|
||||||
|
aws_security_group.firewall
|
||||||
|
openstack_floating_ip.random
|
||||||
|
provider.aws
|
||||||
|
provider.openstack
|
||||||
|
var.foo
|
||||||
openstack_floating_ip.random
|
openstack_floating_ip.random
|
||||||
provider.openstack
|
provider.openstack
|
||||||
provider.aws
|
provider.aws
|
||||||
|
@ -75,6 +83,7 @@ provider.openstack (close)
|
||||||
openstack_floating_ip.random
|
openstack_floating_ip.random
|
||||||
provider.openstack
|
provider.openstack
|
||||||
root
|
root
|
||||||
|
meta.count-boundary (count boundary fixup)
|
||||||
provider.aws (close)
|
provider.aws (close)
|
||||||
provider.openstack (close)
|
provider.openstack (close)
|
||||||
var.foo
|
var.foo
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
resource "aws_instance" "foo" {
|
||||||
|
count = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "bar" {
|
||||||
|
count = "${length(aws_instance.foo.*.id)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "aws_instance" "baz" {
|
||||||
|
count = "${length(aws_instance.bar.*.id)}"
|
||||||
|
}
|
Loading…
Reference in New Issue