2016-09-13 12:56:37 -05:00
|
|
|
package terraform
|
|
|
|
|
|
|
|
import (
|
2018-09-20 14:30:52 -05:00
|
|
|
"fmt"
|
2016-09-13 12:56:37 -05:00
|
|
|
"strings"
|
|
|
|
"testing"
|
2018-05-04 21:24:06 -05:00
|
|
|
|
|
|
|
"github.com/hashicorp/terraform/addrs"
|
2018-09-20 14:30:52 -05:00
|
|
|
"github.com/hashicorp/terraform/plans"
|
|
|
|
"github.com/hashicorp/terraform/states"
|
2019-10-29 09:57:38 -05:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
2016-09-13 12:56:37 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestApplyGraphBuilder_impl(t *testing.T) {
|
|
|
|
var _ GraphBuilder = new(ApplyGraphBuilder)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestApplyGraphBuilder(t *testing.T) {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.create"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Create,
|
2016-09-13 12:56:37 -05:00
|
|
|
},
|
|
|
|
},
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.other"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Update,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("module.child.test_object.create"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Create,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("module.child.test_object.other"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Create,
|
2016-09-14 16:43:14 -05:00
|
|
|
},
|
|
|
|
},
|
2016-09-13 12:56:37 -05:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
b := &ApplyGraphBuilder{
|
2018-09-24 18:25:40 -05:00
|
|
|
Config: testModule(t, "graph-builder-apply-basic"),
|
|
|
|
Changes: changes,
|
|
|
|
Components: simpleMockComponentFactory(),
|
|
|
|
Schemas: simpleTestSchemas(),
|
2016-09-13 12:56:37 -05:00
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
2016-09-13 12:56:37 -05:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
if g.Path.String() != addrs.RootModuleInstance.String() {
|
|
|
|
t.Fatalf("wrong path %q", g.Path.String())
|
2016-09-13 12:56:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
actual := strings.TrimSpace(g.String())
|
2018-09-24 18:25:40 -05:00
|
|
|
|
2016-09-13 12:56:37 -05:00
|
|
|
expected := strings.TrimSpace(testApplyGraphBuilderStr)
|
|
|
|
if actual != expected {
|
2018-05-10 17:38:58 -05:00
|
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
2016-09-13 12:56:37 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-07 13:49:50 -06:00
|
|
|
// This tests the ordering of two resources where a non-CBD depends
|
|
|
|
// on a CBD. GH-11349.
|
|
|
|
func TestApplyGraphBuilder_depCbd(t *testing.T) {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.A"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
2018-09-21 19:08:52 -05:00
|
|
|
Action: plans.CreateThenDelete,
|
2017-02-13 14:52:45 -06:00
|
|
|
},
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.B"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Update,
|
2017-02-07 13:49:50 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2019-11-19 16:41:45 -06:00
|
|
|
state := states.NewState()
|
|
|
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("test_object.A").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"A"}`),
|
|
|
|
},
|
2020-04-01 12:13:40 -05:00
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
2019-11-19 16:41:45 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("test_object.B").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"B","test_list":["x"]}`),
|
2020-09-24 10:57:43 -05:00
|
|
|
Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
|
2019-11-19 16:41:45 -06:00
|
|
|
},
|
2020-04-01 12:13:40 -05:00
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
2019-11-19 16:41:45 -06:00
|
|
|
)
|
|
|
|
|
2017-02-07 13:49:50 -06:00
|
|
|
b := &ApplyGraphBuilder{
|
2018-09-24 18:25:40 -05:00
|
|
|
Config: testModule(t, "graph-builder-apply-dep-cbd"),
|
|
|
|
Changes: changes,
|
|
|
|
Components: simpleMockComponentFactory(),
|
|
|
|
Schemas: simpleTestSchemas(),
|
2019-11-19 16:41:45 -06:00
|
|
|
State: state,
|
2017-02-07 13:49:50 -06:00
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
2017-02-07 13:49:50 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
if g.Path.String() != addrs.RootModuleInstance.String() {
|
|
|
|
t.Fatalf("wrong path %q", g.Path.String())
|
2017-02-07 13:49:50 -06:00
|
|
|
}
|
|
|
|
|
2018-09-20 14:30:52 -05:00
|
|
|
// We're going to go hunting for our deposed instance node here, so we
|
|
|
|
// can find out its key to use in the assertions below.
|
|
|
|
var dk states.DeposedKey
|
|
|
|
for _, v := range g.Vertices() {
|
|
|
|
tv, ok := v.(*NodeDestroyDeposedResourceInstanceObject)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if dk != states.NotDeposed {
|
|
|
|
t.Fatalf("more than one deposed instance node in the graph; want only one")
|
|
|
|
}
|
|
|
|
dk = tv.DeposedKey
|
|
|
|
}
|
|
|
|
if dk == states.NotDeposed {
|
|
|
|
t.Fatalf("no deposed instance node in the graph; want one")
|
|
|
|
}
|
|
|
|
|
2018-09-24 18:25:40 -05:00
|
|
|
destroyName := fmt.Sprintf("test_object.A (destroy deposed %s)", dk)
|
2018-09-20 14:30:52 -05:00
|
|
|
|
2017-02-07 13:49:50 -06:00
|
|
|
// Create A, Modify B, Destroy A
|
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
2018-05-10 17:38:58 -05:00
|
|
|
"test_object.A",
|
2018-09-20 14:30:52 -05:00
|
|
|
destroyName,
|
|
|
|
)
|
2017-02-07 14:01:27 -06:00
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
2018-05-10 17:38:58 -05:00
|
|
|
"test_object.A",
|
2018-09-20 14:30:52 -05:00
|
|
|
"test_object.B",
|
|
|
|
)
|
2017-02-07 13:49:50 -06:00
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
2018-05-10 17:38:58 -05:00
|
|
|
"test_object.B",
|
2018-09-20 14:30:52 -05:00
|
|
|
destroyName,
|
|
|
|
)
|
2017-02-07 13:49:50 -06:00
|
|
|
}
|
|
|
|
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
// This tests the ordering of two resources that are both CBD that
|
|
|
|
// require destroy/create.
|
|
|
|
func TestApplyGraphBuilder_doubleCBD(t *testing.T) {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.A"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
2018-09-21 19:08:52 -05:00
|
|
|
Action: plans.CreateThenDelete,
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.B"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
2018-09-21 19:08:52 -05:00
|
|
|
Action: plans.CreateThenDelete,
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
b := &ApplyGraphBuilder{
|
2020-08-14 13:13:33 -05:00
|
|
|
Config: testModule(t, "graph-builder-apply-double-cbd"),
|
|
|
|
Changes: changes,
|
|
|
|
Components: simpleMockComponentFactory(),
|
|
|
|
Schemas: simpleTestSchemas(),
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
if g.Path.String() != addrs.RootModuleInstance.String() {
|
|
|
|
t.Fatalf("wrong path %q", g.Path.String())
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
}
|
|
|
|
|
2018-09-25 09:42:44 -05:00
|
|
|
// We're going to go hunting for our deposed instance node here, so we
|
|
|
|
// can find out its key to use in the assertions below.
|
|
|
|
var destroyA, destroyB string
|
|
|
|
for _, v := range g.Vertices() {
|
|
|
|
tv, ok := v.(*NodeDestroyDeposedResourceInstanceObject)
|
|
|
|
if !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-03-23 07:56:55 -05:00
|
|
|
switch tv.Addr.Resource.Resource.Name {
|
2018-09-25 09:42:44 -05:00
|
|
|
case "A":
|
|
|
|
destroyA = fmt.Sprintf("test_object.A (destroy deposed %s)", tv.DeposedKey)
|
|
|
|
case "B":
|
|
|
|
destroyB = fmt.Sprintf("test_object.B (destroy deposed %s)", tv.DeposedKey)
|
|
|
|
default:
|
|
|
|
t.Fatalf("unknown instance: %s", tv.Addr)
|
|
|
|
}
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
}
|
2018-09-25 09:42:44 -05:00
|
|
|
|
|
|
|
// Create A, Modify B, Destroy A
|
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
|
|
|
"test_object.A",
|
|
|
|
destroyA,
|
|
|
|
)
|
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
|
|
|
"test_object.A",
|
|
|
|
"test_object.B",
|
|
|
|
)
|
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
|
|
|
"test_object.B",
|
|
|
|
destroyB,
|
|
|
|
)
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
}
|
|
|
|
|
2017-02-07 22:14:38 -06:00
|
|
|
// This tests the ordering of two resources being destroyed that depend
|
|
|
|
// on each other from only state. GH-11749
|
|
|
|
func TestApplyGraphBuilder_destroyStateOnly(t *testing.T) {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("module.child.test_object.A"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Delete,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("module.child.test_object.B"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Delete,
|
2017-02-07 22:14:38 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2019-12-03 15:04:25 -06:00
|
|
|
state := states.NewState()
|
|
|
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
|
|
|
child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey))
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("test_object.A").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
|
|
|
},
|
2020-04-01 12:13:40 -05:00
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
2019-12-03 15:04:25 -06:00
|
|
|
)
|
|
|
|
child.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("test_object.B").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
2020-09-24 10:57:43 -05:00
|
|
|
Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("module.child.test_object.A")},
|
2017-02-07 22:14:38 -06:00
|
|
|
},
|
2020-04-01 12:13:40 -05:00
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
2019-12-03 15:04:25 -06:00
|
|
|
)
|
2017-02-07 22:14:38 -06:00
|
|
|
|
|
|
|
b := &ApplyGraphBuilder{
|
2020-08-14 13:13:33 -05:00
|
|
|
Config: testModule(t, "empty"),
|
|
|
|
Changes: changes,
|
|
|
|
State: state,
|
|
|
|
Components: simpleMockComponentFactory(),
|
|
|
|
Schemas: simpleTestSchemas(),
|
2017-02-07 22:14:38 -06:00
|
|
|
}
|
|
|
|
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
g, diags := b.Build(addrs.RootModuleInstance)
|
|
|
|
if diags.HasErrors() {
|
|
|
|
t.Fatalf("err: %s", diags.Err())
|
2017-02-07 22:14:38 -06:00
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
if g.Path.String() != addrs.RootModuleInstance.String() {
|
|
|
|
t.Fatalf("wrong path %q", g.Path.String())
|
2017-02-07 22:14:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
2018-05-10 17:38:58 -05:00
|
|
|
"module.child.test_object.B (destroy)",
|
|
|
|
"module.child.test_object.A (destroy)")
|
2017-02-07 22:14:38 -06:00
|
|
|
}
|
|
|
|
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
// This tests the ordering of destroying a single count of a resource.
|
|
|
|
func TestApplyGraphBuilder_destroyCount(t *testing.T) {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.A[1]"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Delete,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.B"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Update,
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2020-07-16 08:09:18 -05:00
|
|
|
state := states.NewState()
|
|
|
|
root := state.RootModule()
|
|
|
|
addrA := mustResourceInstanceAddr("test_object.A[1]")
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
addrA.Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"B"}`),
|
|
|
|
},
|
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("test_object.B").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"B"}`),
|
|
|
|
Dependencies: []addrs.ConfigResource{addrA.ContainingResource().Config()},
|
|
|
|
},
|
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
|
|
|
)
|
|
|
|
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
b := &ApplyGraphBuilder{
|
2018-09-25 09:42:44 -05:00
|
|
|
Config: testModule(t, "graph-builder-apply-count"),
|
|
|
|
Changes: changes,
|
|
|
|
Components: simpleMockComponentFactory(),
|
|
|
|
Schemas: simpleTestSchemas(),
|
2020-07-16 08:09:18 -05:00
|
|
|
State: state,
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
if g.Path.String() != addrs.RootModuleInstance.String() {
|
|
|
|
t.Fatalf("wrong module path %q", g.Path)
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
actual := strings.TrimSpace(g.String())
|
|
|
|
expected := strings.TrimSpace(testApplyGraphBuilderDestroyCountStr)
|
|
|
|
if actual != expected {
|
2018-05-10 17:38:58 -05:00
|
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-14 23:39:13 -06:00
|
|
|
func TestApplyGraphBuilder_moduleDestroy(t *testing.T) {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("module.A.test_object.foo"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Delete,
|
2016-12-14 23:39:13 -06:00
|
|
|
},
|
|
|
|
},
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("module.B.test_object.foo"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Delete,
|
2016-12-14 23:39:13 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2019-12-03 15:04:25 -06:00
|
|
|
state := states.NewState()
|
|
|
|
modA := state.EnsureModule(addrs.RootModuleInstance.Child("A", addrs.NoKey))
|
|
|
|
modA.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("test_object.foo").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo"}`),
|
|
|
|
},
|
2020-04-01 12:13:40 -05:00
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
2019-12-03 15:04:25 -06:00
|
|
|
)
|
|
|
|
modB := state.EnsureModule(addrs.RootModuleInstance.Child("B", addrs.NoKey))
|
|
|
|
modB.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("test_object.foo").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"foo","value":"foo"}`),
|
2020-09-24 10:57:43 -05:00
|
|
|
Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("module.A.test_object.foo")},
|
2019-12-03 15:04:25 -06:00
|
|
|
},
|
2020-04-01 12:13:40 -05:00
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
2019-12-03 15:04:25 -06:00
|
|
|
)
|
|
|
|
|
2016-12-14 23:39:13 -06:00
|
|
|
b := &ApplyGraphBuilder{
|
2018-05-10 17:38:58 -05:00
|
|
|
Config: testModule(t, "graph-builder-apply-module-destroy"),
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
Changes: changes,
|
2018-05-10 17:38:58 -05:00
|
|
|
Components: simpleMockComponentFactory(),
|
2018-05-31 14:39:45 -05:00
|
|
|
Schemas: simpleTestSchemas(),
|
2019-12-03 15:04:25 -06:00
|
|
|
State: state,
|
2016-12-14 23:39:13 -06:00
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
2016-12-14 23:39:13 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
2018-05-10 17:38:58 -05:00
|
|
|
"module.B.test_object.foo (destroy)",
|
|
|
|
"module.A.test_object.foo (destroy)",
|
|
|
|
)
|
2016-12-14 23:39:13 -06:00
|
|
|
}
|
|
|
|
|
2017-01-20 20:07:51 -06:00
|
|
|
func TestApplyGraphBuilder_provisioner(t *testing.T) {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.foo"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Create,
|
2017-01-20 20:07:51 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
b := &ApplyGraphBuilder{
|
2018-05-10 17:38:58 -05:00
|
|
|
Config: testModule(t, "graph-builder-apply-provisioner"),
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
Changes: changes,
|
2018-05-10 17:38:58 -05:00
|
|
|
Components: simpleMockComponentFactory(),
|
2018-05-31 14:39:45 -05:00
|
|
|
Schemas: simpleTestSchemas(),
|
2017-01-20 20:07:51 -06:00
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
2017-01-20 20:07:51 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-05-10 17:38:58 -05:00
|
|
|
testGraphContains(t, g, "provisioner.test")
|
2017-01-20 20:07:51 -06:00
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
2018-05-10 17:38:58 -05:00
|
|
|
"provisioner.test",
|
|
|
|
"test_object.foo",
|
|
|
|
)
|
2017-01-20 20:07:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestApplyGraphBuilder_provisionerDestroy(t *testing.T) {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.foo"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Delete,
|
2017-01-20 20:07:51 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
b := &ApplyGraphBuilder{
|
2018-05-10 17:38:58 -05:00
|
|
|
Config: testModule(t, "graph-builder-apply-provisioner"),
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
Changes: changes,
|
2018-05-10 17:38:58 -05:00
|
|
|
Components: simpleMockComponentFactory(),
|
2018-05-31 14:39:45 -05:00
|
|
|
Schemas: simpleTestSchemas(),
|
2017-01-20 20:07:51 -06:00
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
2017-01-20 20:07:51 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-05-10 17:38:58 -05:00
|
|
|
testGraphContains(t, g, "provisioner.test")
|
2017-01-20 20:07:51 -06:00
|
|
|
testGraphHappensBefore(
|
|
|
|
t, g,
|
2018-05-10 17:38:58 -05:00
|
|
|
"provisioner.test",
|
|
|
|
"test_object.foo (destroy)",
|
|
|
|
)
|
2017-01-20 20:07:51 -06:00
|
|
|
}
|
|
|
|
|
2017-02-13 14:52:45 -06:00
|
|
|
func TestApplyGraphBuilder_targetModule(t *testing.T) {
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.foo"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Update,
|
2017-02-13 14:52:45 -06:00
|
|
|
},
|
|
|
|
},
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("module.child2.test_object.foo"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Update,
|
2017-02-13 14:52:45 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
b := &ApplyGraphBuilder{
|
2018-05-10 17:38:58 -05:00
|
|
|
Config: testModule(t, "graph-builder-apply-target-module"),
|
terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.
The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.
The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.
Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-08-14 16:24:45 -05:00
|
|
|
Changes: changes,
|
2018-05-10 17:38:58 -05:00
|
|
|
Components: simpleMockComponentFactory(),
|
2018-05-31 14:39:45 -05:00
|
|
|
Schemas: simpleTestSchemas(),
|
2018-05-04 21:24:06 -05:00
|
|
|
Targets: []addrs.Targetable{
|
|
|
|
addrs.RootModuleInstance.Child("child2", addrs.NoKey),
|
|
|
|
},
|
2017-02-13 14:52:45 -06:00
|
|
|
}
|
|
|
|
|
2018-05-04 21:24:06 -05:00
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
2017-02-13 14:52:45 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
testGraphNotContains(t, g, "module.child1.output.instance_id")
|
|
|
|
}
|
|
|
|
|
2019-10-29 09:57:38 -05:00
|
|
|
// Ensure that an update resulting from the removal of a resource happens after
|
|
|
|
// that resource is destroyed.
|
|
|
|
func TestApplyGraphBuilder_updateFromOrphan(t *testing.T) {
|
|
|
|
schemas := simpleTestSchemas()
|
2020-04-01 12:13:40 -05:00
|
|
|
instanceSchema := schemas.Providers[addrs.NewDefaultProvider("test")].ResourceTypes["test_object"]
|
2019-10-29 09:57:38 -05:00
|
|
|
|
|
|
|
bBefore, _ := plans.NewDynamicValue(
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("b_id"),
|
|
|
|
"test_string": cty.StringVal("a_id"),
|
|
|
|
}), instanceSchema.ImpliedType())
|
|
|
|
bAfter, _ := plans.NewDynamicValue(
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("b_id"),
|
|
|
|
"test_string": cty.StringVal("changed"),
|
|
|
|
}), instanceSchema.ImpliedType())
|
|
|
|
|
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.a"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Delete,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.b"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Update,
|
|
|
|
Before: bBefore,
|
|
|
|
After: bAfter,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
state := states.NewState()
|
|
|
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
addrs.Resource{
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Type: "test_object",
|
|
|
|
Name: "a",
|
|
|
|
}.Instance(addrs.NoKey),
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"a_id"}`),
|
|
|
|
},
|
2020-02-13 14:32:58 -06:00
|
|
|
addrs.AbsProviderConfig{
|
2020-04-01 12:13:40 -05:00
|
|
|
Provider: addrs.NewDefaultProvider("test"),
|
2020-03-10 20:21:19 -05:00
|
|
|
Module: addrs.RootModule,
|
2020-02-13 14:32:58 -06:00
|
|
|
},
|
2019-10-29 09:57:38 -05:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
addrs.Resource{
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Type: "test_object",
|
|
|
|
Name: "b",
|
|
|
|
}.Instance(addrs.NoKey),
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"b_id","test_string":"a_id"}`),
|
2020-03-23 14:26:18 -05:00
|
|
|
Dependencies: []addrs.ConfigResource{
|
|
|
|
addrs.ConfigResource{
|
2019-10-29 09:57:38 -05:00
|
|
|
Resource: addrs.Resource{
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Type: "test_object",
|
|
|
|
Name: "a",
|
|
|
|
},
|
2020-03-23 14:26:18 -05:00
|
|
|
Module: root.Addr.Module(),
|
2019-10-29 09:57:38 -05:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2020-02-13 14:32:58 -06:00
|
|
|
addrs.AbsProviderConfig{
|
2020-04-01 12:13:40 -05:00
|
|
|
Provider: addrs.NewDefaultProvider("test"),
|
2020-03-10 20:21:19 -05:00
|
|
|
Module: addrs.RootModule,
|
2020-02-13 14:32:58 -06:00
|
|
|
},
|
2019-10-29 09:57:38 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
b := &ApplyGraphBuilder{
|
|
|
|
Config: testModule(t, "graph-builder-apply-orphan-update"),
|
|
|
|
Changes: changes,
|
|
|
|
Components: simpleMockComponentFactory(),
|
|
|
|
Schemas: schemas,
|
|
|
|
State: state,
|
|
|
|
}
|
|
|
|
|
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := strings.TrimSpace(`
|
|
|
|
test_object.a (destroy)
|
|
|
|
test_object.b
|
|
|
|
test_object.a (destroy)
|
|
|
|
`)
|
|
|
|
|
|
|
|
instanceGraph := filterInstances(g)
|
2019-10-30 14:59:34 -05:00
|
|
|
got := strings.TrimSpace(instanceGraph.String())
|
2019-12-12 14:02:26 -06:00
|
|
|
|
|
|
|
if got != expected {
|
|
|
|
t.Fatalf("expected:\n%s\ngot:\n%s", expected, got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that an update resulting from the removal of a resource happens before
|
|
|
|
// a CBD resource is destroyed.
|
|
|
|
func TestApplyGraphBuilder_updateFromCBDOrphan(t *testing.T) {
|
|
|
|
schemas := simpleTestSchemas()
|
2020-04-01 12:13:40 -05:00
|
|
|
instanceSchema := schemas.Providers[addrs.NewDefaultProvider("test")].ResourceTypes["test_object"]
|
2019-12-12 14:02:26 -06:00
|
|
|
|
|
|
|
bBefore, _ := plans.NewDynamicValue(
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("b_id"),
|
|
|
|
"test_string": cty.StringVal("a_id"),
|
|
|
|
}), instanceSchema.ImpliedType())
|
|
|
|
bAfter, _ := plans.NewDynamicValue(
|
|
|
|
cty.ObjectVal(map[string]cty.Value{
|
|
|
|
"id": cty.StringVal("b_id"),
|
|
|
|
"test_string": cty.StringVal("changed"),
|
|
|
|
}), instanceSchema.ImpliedType())
|
|
|
|
|
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.a"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Delete,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.b"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Update,
|
|
|
|
Before: bBefore,
|
|
|
|
After: bAfter,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
state := states.NewState()
|
|
|
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
addrs.Resource{
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Type: "test_object",
|
|
|
|
Name: "a",
|
|
|
|
}.Instance(addrs.NoKey),
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"a_id"}`),
|
|
|
|
CreateBeforeDestroy: true,
|
|
|
|
},
|
2020-04-01 12:13:40 -05:00
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
2019-12-12 14:02:26 -06:00
|
|
|
)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
addrs.Resource{
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Type: "test_object",
|
|
|
|
Name: "b",
|
|
|
|
}.Instance(addrs.NoKey),
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"b_id","test_string":"a_id"}`),
|
2020-03-23 14:26:18 -05:00
|
|
|
Dependencies: []addrs.ConfigResource{
|
|
|
|
addrs.ConfigResource{
|
2019-12-12 14:02:26 -06:00
|
|
|
Resource: addrs.Resource{
|
|
|
|
Mode: addrs.ManagedResourceMode,
|
|
|
|
Type: "test_object",
|
|
|
|
Name: "a",
|
|
|
|
},
|
2020-03-23 14:26:18 -05:00
|
|
|
Module: root.Addr.Module(),
|
2019-12-12 14:02:26 -06:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2020-04-01 12:13:40 -05:00
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
2019-12-12 14:02:26 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
b := &ApplyGraphBuilder{
|
|
|
|
Config: testModule(t, "graph-builder-apply-orphan-update"),
|
|
|
|
Changes: changes,
|
|
|
|
Components: simpleMockComponentFactory(),
|
|
|
|
Schemas: schemas,
|
|
|
|
State: state,
|
|
|
|
}
|
|
|
|
|
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
expected := strings.TrimSpace(`
|
|
|
|
test_object.a (destroy)
|
|
|
|
test_object.b
|
|
|
|
test_object.b
|
|
|
|
`)
|
|
|
|
|
|
|
|
instanceGraph := filterInstances(g)
|
2019-10-30 14:59:34 -05:00
|
|
|
got := strings.TrimSpace(instanceGraph.String())
|
|
|
|
|
|
|
|
if got != expected {
|
|
|
|
t.Fatalf("expected:\n%s\ngot:\n%s", expected, got)
|
2019-10-29 09:57:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-16 16:45:34 -06:00
|
|
|
// The orphan clean up node should not be connected to a provider
|
|
|
|
func TestApplyGraphBuilder_orphanedWithProvider(t *testing.T) {
|
|
|
|
changes := &plans.Changes{
|
|
|
|
Resources: []*plans.ResourceInstanceChangeSrc{
|
|
|
|
{
|
|
|
|
Addr: mustResourceInstanceAddr("test_object.A"),
|
|
|
|
ChangeSrc: plans.ChangeSrc{
|
|
|
|
Action: plans.Delete,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
state := states.NewState()
|
|
|
|
root := state.EnsureModule(addrs.RootModuleInstance)
|
|
|
|
root.SetResourceInstanceCurrent(
|
|
|
|
mustResourceInstanceAddr("test_object.A").Resource,
|
|
|
|
&states.ResourceInstanceObjectSrc{
|
|
|
|
Status: states.ObjectReady,
|
|
|
|
AttrsJSON: []byte(`{"id":"A"}`),
|
|
|
|
},
|
2020-04-01 12:13:40 -05:00
|
|
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"].foo`),
|
2019-12-16 16:45:34 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
b := &ApplyGraphBuilder{
|
|
|
|
Config: testModule(t, "graph-builder-orphan-alias"),
|
|
|
|
Changes: changes,
|
|
|
|
Components: simpleMockComponentFactory(),
|
|
|
|
Schemas: simpleTestSchemas(),
|
|
|
|
State: state,
|
|
|
|
}
|
|
|
|
|
|
|
|
g, err := b.Build(addrs.RootModuleInstance)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The cleanup node has no state or config of its own, so would create a
|
|
|
|
// default provider which we don't want.
|
|
|
|
testGraphNotContains(t, g, "provider.test")
|
|
|
|
}
|
|
|
|
|
2016-09-13 12:56:37 -05:00
|
|
|
const testApplyGraphBuilderStr = `
|
2018-09-07 18:58:32 -05:00
|
|
|
meta.count-boundary (EachMode fixup)
|
2020-04-02 12:43:23 -05:00
|
|
|
module.child (close)
|
2018-05-10 17:38:58 -05:00
|
|
|
test_object.other
|
2020-04-02 12:43:23 -05:00
|
|
|
module.child (close)
|
|
|
|
module.child.test_object.other
|
2020-05-12 09:54:43 -05:00
|
|
|
module.child (expand)
|
2018-05-10 17:38:58 -05:00
|
|
|
module.child.test_object.create
|
2020-05-12 09:54:43 -05:00
|
|
|
module.child.test_object.create (expand)
|
|
|
|
module.child.test_object.create (expand)
|
|
|
|
module.child (expand)
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"]
|
2019-08-21 18:32:14 -05:00
|
|
|
provisioner.test
|
2018-05-10 17:38:58 -05:00
|
|
|
module.child.test_object.other
|
|
|
|
module.child.test_object.create
|
2020-05-12 09:54:43 -05:00
|
|
|
module.child.test_object.other (expand)
|
|
|
|
module.child.test_object.other (expand)
|
|
|
|
module.child (expand)
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"]
|
|
|
|
provider["registry.terraform.io/hashicorp/test"]
|
|
|
|
provider["registry.terraform.io/hashicorp/test"] (close)
|
2018-05-10 17:38:58 -05:00
|
|
|
module.child.test_object.other
|
|
|
|
test_object.other
|
2019-08-21 18:32:14 -05:00
|
|
|
provisioner.test
|
2018-05-10 17:38:58 -05:00
|
|
|
provisioner.test (close)
|
|
|
|
module.child.test_object.create
|
2017-04-12 16:25:15 -05:00
|
|
|
root
|
2018-09-07 18:58:32 -05:00
|
|
|
meta.count-boundary (EachMode fixup)
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"] (close)
|
2018-05-10 17:38:58 -05:00
|
|
|
provisioner.test (close)
|
|
|
|
test_object.create
|
2020-05-12 09:54:43 -05:00
|
|
|
test_object.create (expand)
|
|
|
|
test_object.create (expand)
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"]
|
2018-05-10 17:38:58 -05:00
|
|
|
test_object.other
|
|
|
|
test_object.create
|
2020-05-12 09:54:43 -05:00
|
|
|
test_object.other (expand)
|
|
|
|
test_object.other (expand)
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"]
|
2016-09-13 12:56:37 -05:00
|
|
|
`
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
|
|
|
|
const testApplyGraphBuilderDestroyCountStr = `
|
2018-09-07 18:58:32 -05:00
|
|
|
meta.count-boundary (EachMode fixup)
|
2018-05-10 17:38:58 -05:00
|
|
|
test_object.B
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"]
|
|
|
|
provider["registry.terraform.io/hashicorp/test"] (close)
|
2018-05-10 17:38:58 -05:00
|
|
|
test_object.B
|
2017-04-12 16:25:15 -05:00
|
|
|
root
|
2018-09-07 18:58:32 -05:00
|
|
|
meta.count-boundary (EachMode fixup)
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"] (close)
|
2020-05-12 09:54:43 -05:00
|
|
|
test_object.A (expand)
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"]
|
2018-09-25 09:42:44 -05:00
|
|
|
test_object.A[1] (destroy)
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"]
|
2018-05-10 17:38:58 -05:00
|
|
|
test_object.B
|
2020-05-12 09:54:43 -05:00
|
|
|
test_object.A (expand)
|
2018-05-10 17:38:58 -05:00
|
|
|
test_object.A[1] (destroy)
|
2020-05-12 09:54:43 -05:00
|
|
|
test_object.B (expand)
|
|
|
|
test_object.B (expand)
|
2020-04-01 12:13:40 -05:00
|
|
|
provider["registry.terraform.io/hashicorp/test"]
|
terraform: apply resource must depend on destroy deps
Fixes #10440
This updates the behavior of "apply" resources to depend on the
destroy versions of their dependencies.
We make an exception to this behavior when the "apply" resource is CBD.
This is odd and not 100% correct, but it mimics the behavior of the
legacy graphs and avoids us having to do major core work to support the
100% correct solution.
I'll explain this in examples...
Given the following configuration:
resource "null_resource" "a" {
count = "${var.count}"
}
resource "null_resource" "b" {
triggers { key = "${join(",", null_resource.a.*.id)}" }
}
Assume we've successfully created this configuration with count = 2.
When going from count = 2 to count = 1, `null_resource.b` should wait
for `null_resource.a.1` to destroy.
If it doesn't, then it is a race: depending when we interpolate the
`triggers.key` attribute of `null_resource.b`, we may get 1 value or 2.
If `null_resource.a.1` is destroyed, we'll get 1. Otherwise, we'll get
2. This was the root cause of #10440
In the legacy graphs, `null_resource.b` would depend on the destruction
of any `null_resource.a` (orphans, tainted, anything!). This would
ensure proper ordering. We mimic that behavior here.
The difference is CBD. If `null_resource.b` has CBD enabled, then the
ordering **in the legacy graph** becomes:
1. null_resource.b (create)
2. null_resource.b (destroy)
3. null_resource.a (destroy)
In this case, the update would always have 2 values for `triggers.key`,
even though we were destroying a resource later! This scenario required
two `terraform apply` operations.
This is what the CBD check is for in this PR. We do this to mimic the
behavior of the legacy graph.
The correct solution to do one day is to allow splat references
(`null_resource.a.*.id`) to happen in parallel and only read up to to
the `count` amount in the state. This requires some fairly significant
work close to the 0.8 release date, so we can defer this to later and
adopt the 0.7.x behavior for now.
2016-12-04 01:44:09 -06:00
|
|
|
`
|