core: Pass components through to the destroy transformers

These transformers both construct temporary graphs using many of the same
transformers used in the apply graph, and properly doing this now requires
access to the providers and provisioners in order to obtain their schemas.

Along with this, we also update the tests here to use the
simpleMockComponentFactory helper to get a mock provider with a schema
already configured, which means we also need to update the test fixtures
and assertions to use the resource type and attributes defined in that
mock factory.
This commit is contained in:
Martin Atkins 2018-05-09 17:05:18 -07:00
parent 1a9ee72ecb
commit bec0f56808
13 changed files with 254 additions and 170 deletions

View File

@ -289,10 +289,11 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags.
case GraphTypePlanDestroy: case GraphTypePlanDestroy:
return (&DestroyPlanGraphBuilder{ return (&DestroyPlanGraphBuilder{
Config: c.config, Config: c.config,
State: c.state, State: c.state,
Targets: c.targets, Components: c.components,
Validate: opts.Validate, Targets: c.targets,
Validate: opts.Validate,
}).Build(addrs.RootModuleInstance) }).Build(addrs.RootModuleInstance)
case GraphTypeRefresh: case GraphTypeRefresh:

View File

@ -88,10 +88,18 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config),
// Destruction ordering // Destruction ordering
&DestroyEdgeTransformer{Config: b.Config, State: b.State}, &DestroyEdgeTransformer{
Config: b.Config,
State: b.State,
Components: b.Components,
},
GraphTransformIf( GraphTransformIf(
func() bool { return !b.Destroy }, func() bool { return !b.Destroy },
&CBDEdgeTransformer{Config: b.Config, State: b.State}, &CBDEdgeTransformer{
Config: b.Config,
State: b.State,
Components: b.Components,
},
), ),
// Provisioner-related transformations // Provisioner-related transformations

View File

@ -22,6 +22,10 @@ type DestroyPlanGraphBuilder struct {
// Targets are resources to target // Targets are resources to target
Targets []addrs.Targetable Targets []addrs.Targetable
// Components is a factory for the plug-in components (providers and
// provisioners) available for use.
Components contextComponentFactory
// Validate will do structural validation of the graph. // Validate will do structural validation of the graph.
Validate bool Validate bool
} }
@ -55,7 +59,11 @@ func (b *DestroyPlanGraphBuilder) Steps() []GraphTransformer {
// Destruction ordering. We require this only so that // Destruction ordering. We require this only so that
// targeting below will prune the correct things. // targeting below will prune the correct things.
&DestroyEdgeTransformer{Config: b.Config, State: b.State}, &DestroyEdgeTransformer{
Config: b.Config,
State: b.State,
Components: b.Components,
},
// Target. Note we don't set "Destroy: true" here since we already // Target. Note we don't set "Destroy: true" here since we already
// created proper destroy ordering. // created proper destroy ordering.

View File

@ -1,5 +1,5 @@
resource "test" "A" {} resource "test_object" "A" {}
resource "test" "B" { resource "test_object" "B" {
value = "${test.A.value}" test_string = "${test_object.A.test_string}"
} }

View File

@ -1,8 +1,9 @@
resource "aws_instance" "a" {} resource "test_object" "a" {}
resource "aws_instance" "b" {
value = "${aws_instance.a.id}" resource "test_object" "b" {
test_string = "${test_object.a.test_string}"
} }
resource "aws_instance" "c" { resource "test_object" "c" {
value = "${aws_instance.b.id}" test_string = "${test_object.b.test_string}"
} }

View File

@ -1,7 +1,7 @@
resource "aws_instance" "b" { resource "test_object" "b" {
value = "foo" test_string = "foo"
} }
output "output" { output "output" {
value = "${aws_instance.b.value}" value = "${test_object.b.test_string}"
} }

View File

@ -1,7 +1,7 @@
resource "aws_instance" "a" { resource "test_object" "a" {
value = "${module.child.output}" test_string = "${module.child.output}"
} }
module "child" { module "child" {
source = "./child" source = "./child"
} }

View File

@ -1,9 +1,9 @@
resource "test" "A" {} resource "test_object" "A" {}
resource "test" "B" { resource "test_object" "B" {
value = "${test.A.value}" test_string = "${test_object.A.test_string}"
} }
resource "test" "C" { resource "test_object" "C" {
value = "${test.B.value}" test_string = "${test_object.B.test_string}"
} }

View File

@ -1,5 +1,6 @@
resource "test" "A" {} resource "test_object" "A" {}
resource "test" "B" {
count = 2 resource "test_object" "B" {
value = "${test.A.*.value}" count = 2
test_string = "${test_object.A.*.test_string}"
} }

View File

@ -40,11 +40,14 @@ type CBDEdgeTransformer struct {
// any way possible. Either can be nil if not availabile. // any way possible. Either can be nil if not availabile.
Config *configs.Config Config *configs.Config
State *State State *State
// If configuration is present then Components is required in order to
// obtain schema information from providers and provisioners in order
// to properly resolve implicit dependencies.
Components contextComponentFactory
} }
func (t *CBDEdgeTransformer) Transform(g *Graph) error { func (t *CBDEdgeTransformer) Transform(g *Graph) error {
log.Printf("[TRACE] CBDEdgeTransformer: Beginning CBD transformation...")
// Go through and reverse any destroy edges // Go through and reverse any destroy edges
destroyMap := make(map[string][]dag.Vertex) destroyMap := make(map[string][]dag.Vertex)
for _, v := range g.Vertices() { for _, v := range g.Vertices() {
@ -64,6 +67,7 @@ func (t *CBDEdgeTransformer) Transform(g *Graph) error {
// and we need to auto-upgrade this node to CBD. We do this because // and we need to auto-upgrade this node to CBD. We do this because
// a CBD node depending on non-CBD will result in cycles. To avoid this, // a CBD node depending on non-CBD will result in cycles. To avoid this,
// we always attempt to upgrade it. // we always attempt to upgrade it.
log.Printf("[TRACE] CBDEdgeTransformer: forcing create_before_destroy on for %q (%T)", dag.VertexName(v), v)
if err := dn.ModifyCreateBeforeDestroy(true); err != nil { if err := dn.ModifyCreateBeforeDestroy(true); err != nil {
return fmt.Errorf( return fmt.Errorf(
"%s: must have create before destroy enabled because "+ "%s: must have create before destroy enabled because "+
@ -174,6 +178,7 @@ func (t *CBDEdgeTransformer) depMap(destroyMap map[string][]dag.Vertex) (map[str
&FlatConfigTransformer{Config: t.Config}, &FlatConfigTransformer{Config: t.Config},
&AttachResourceConfigTransformer{Config: t.Config}, &AttachResourceConfigTransformer{Config: t.Config},
&AttachStateTransformer{State: t.State}, &AttachStateTransformer{State: t.State},
&AttachSchemaTransformer{Components: t.Components},
&ReferenceTransformer{}, &ReferenceTransformer{},
}, },
Name: "CBDEdgeTransformer", Name: "CBDEdgeTransformer",

View File

@ -9,15 +9,16 @@ import (
func TestCBDEdgeTransformer(t *testing.T) { func TestCBDEdgeTransformer(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeCreatorTest{AddrString: "test.A"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.A"})
g.Add(&graphNodeCreatorTest{AddrString: "test.B"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.B"})
g.Add(&graphNodeDestroyerTest{AddrString: "test.A", CBD: true}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A", CBD: true})
module := testModule(t, "transform-destroy-edge-basic") module := testModule(t, "transform-destroy-edge-basic")
{ {
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -25,7 +26,10 @@ func TestCBDEdgeTransformer(t *testing.T) {
} }
{ {
tf := &CBDEdgeTransformer{Config: module} tf := &CBDEdgeTransformer{
Config: module,
Components: simpleMockComponentFactory(),
}
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -34,22 +38,23 @@ func TestCBDEdgeTransformer(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformCBDEdgeBasicStr) expected := strings.TrimSpace(testTransformCBDEdgeBasicStr)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
func TestCBDEdgeTransformer_depNonCBD(t *testing.T) { func TestCBDEdgeTransformer_depNonCBD(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeCreatorTest{AddrString: "test.A"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.A"})
g.Add(&graphNodeCreatorTest{AddrString: "test.B"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.B"})
g.Add(&graphNodeDestroyerTest{AddrString: "test.A"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A"})
g.Add(&graphNodeDestroyerTest{AddrString: "test.B", CBD: true}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B", CBD: true})
module := testModule(t, "transform-destroy-edge-basic") module := testModule(t, "transform-destroy-edge-basic")
{ {
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -57,7 +62,10 @@ func TestCBDEdgeTransformer_depNonCBD(t *testing.T) {
} }
{ {
tf := &CBDEdgeTransformer{Config: module} tf := &CBDEdgeTransformer{
Config: module,
Components: simpleMockComponentFactory(),
}
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -66,22 +74,23 @@ func TestCBDEdgeTransformer_depNonCBD(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformCBDEdgeDepNonCBDStr) expected := strings.TrimSpace(testTransformCBDEdgeDepNonCBDStr)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) { func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeCreatorTest{AddrString: "test.A"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.A"})
g.Add(&graphNodeCreatorTest{AddrString: "test.B[0]"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.B[0]"})
g.Add(&graphNodeCreatorTest{AddrString: "test.B[1]"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.B[1]"})
g.Add(&graphNodeDestroyerTest{AddrString: "test.A", CBD: true}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A", CBD: true})
module := testModule(t, "transform-destroy-edge-splat") module := testModule(t, "transform-destroy-edge-splat")
{ {
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -89,7 +98,10 @@ func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
} }
{ {
tf := &CBDEdgeTransformer{Config: module} tf := &CBDEdgeTransformer{
Config: module,
Components: simpleMockComponentFactory(),
}
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -97,33 +109,34 @@ func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(` expected := strings.TrimSpace(`
test.A test_object.A
test.A (destroy) test_object.A (destroy)
test.A test_object.A
test.B[0] test_object.B[0]
test.B[1] test_object.B[1]
test.B[0] test_object.B[0]
test.B[1] test_object.B[1]
`) `)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) { func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeCreatorTest{AddrString: "test.A[0]"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.A[0]"})
g.Add(&graphNodeCreatorTest{AddrString: "test.A[1]"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.A[1]"})
g.Add(&graphNodeCreatorTest{AddrString: "test.B[0]"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.B[0]"})
g.Add(&graphNodeCreatorTest{AddrString: "test.B[1]"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.B[1]"})
g.Add(&graphNodeDestroyerTest{AddrString: "test.A[0]", CBD: true}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A[0]", CBD: true})
g.Add(&graphNodeDestroyerTest{AddrString: "test.A[1]", CBD: true}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A[1]", CBD: true})
module := testModule(t, "transform-destroy-edge-splat") module := testModule(t, "transform-destroy-edge-splat")
{ {
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: module, Config: module,
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -131,7 +144,10 @@ func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
} }
{ {
tf := &CBDEdgeTransformer{Config: module} tf := &CBDEdgeTransformer{
Config: module,
Components: simpleMockComponentFactory(),
}
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
@ -139,39 +155,39 @@ func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(` expected := strings.TrimSpace(`
test.A[0] test_object.A[0]
test.A[0] (destroy) test_object.A[0] (destroy)
test.A[0] test_object.A[0]
test.B[0] test_object.B[0]
test.B[1] test_object.B[1]
test.A[1] test_object.A[1]
test.A[1] (destroy) test_object.A[1] (destroy)
test.A[1] test_object.A[1]
test.B[0] test_object.B[0]
test.B[1] test_object.B[1]
test.B[0] test_object.B[0]
test.B[1] test_object.B[1]
`) `)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
const testTransformCBDEdgeBasicStr = ` const testTransformCBDEdgeBasicStr = `
test.A test_object.A
test.A (destroy) test_object.A (destroy)
test.A test_object.A
test.B test_object.B
test.B test_object.B
` `
const testTransformCBDEdgeDepNonCBDStr = ` const testTransformCBDEdgeDepNonCBDStr = `
test.A test_object.A
test.A (destroy) (modified) test_object.A (destroy) (modified)
test.A test_object.A
test.B test_object.B
test.B (destroy) test_object.B (destroy)
test.B test_object.B
test.B (destroy) test_object.B (destroy)
test.B test_object.B
` `

View File

@ -44,11 +44,14 @@ type DestroyEdgeTransformer struct {
// to determine what a destroy node depends on. Any of these can be nil. // to determine what a destroy node depends on. Any of these can be nil.
Config *configs.Config Config *configs.Config
State *State State *State
// If configuration is present then Components is required in order to
// obtain schema information from providers and provisioners in order
// to properly resolve implicit dependencies.
Components contextComponentFactory
} }
func (t *DestroyEdgeTransformer) Transform(g *Graph) error { func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
log.Printf("[TRACE] DestroyEdgeTransformer: Beginning destroy edge transformation...")
// Build a map of what is being destroyed (by address string) to // Build a map of what is being destroyed (by address string) to
// the list of destroyers. In general there will only be one destroyer // the list of destroyers. In general there will only be one destroyer
// but to make it more robust we support multiple. // but to make it more robust we support multiple.
@ -67,7 +70,7 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
addr := *addrP addr := *addrP
key := addr.String() key := addr.String()
log.Printf("[TRACE] DestroyEdgeTransformer: %s will destroy %s", dag.VertexName(dn), key) log.Printf("[TRACE] DestroyEdgeTransformer: %q (%T) destroys %s", dag.VertexName(dn), v, key)
destroyers[key] = append(destroyers[key], dn) destroyers[key] = append(destroyers[key], dn)
destroyerAddrs[key] = addr destroyerAddrs[key] = addr
} }
@ -103,7 +106,7 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
a := v a := v
log.Printf( log.Printf(
"[TRACE] DestroyEdgeTransformer: connecting creator/destroyer: %s, %s", "[TRACE] DestroyEdgeTransformer: connecting creator %q with destroyer %q",
dag.VertexName(a), dag.VertexName(a_d)) dag.VertexName(a), dag.VertexName(a_d))
g.Connect(&DestroyEdge{S: a, T: a_d}) g.Connect(&DestroyEdge{S: a, T: a_d})
@ -138,6 +141,10 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
&RootVariableTransformer{Config: t.Config}, &RootVariableTransformer{Config: t.Config},
&ModuleVariableTransformer{Config: t.Config}, &ModuleVariableTransformer{Config: t.Config},
// Must be before ReferenceTransformer, since schema is required to
// extract references from config.
&AttachSchemaTransformer{Components: t.Components},
&ReferenceTransformer{}, &ReferenceTransformer{},
} }
@ -157,12 +164,7 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
// This part is a little bit weird but is the best way to // This part is a little bit weird but is the best way to
// find the dependencies we need to: build a graph and use the // find the dependencies we need to: build a graph and use the
// attach config and state transformers then ask for references. // attach config and state transformers then ask for references.
abstract := &NodeAbstractResourceInstance{ abstract := NewNodeAbstractResourceInstance(addr)
NodeAbstractResource: NodeAbstractResource{
Addr: addr.ContainingResource(),
},
InstanceKey: addr.Resource.Key,
}
tempG.Add(abstract) tempG.Add(abstract)
tempDestroyed = append(tempDestroyed, abstract) tempDestroyed = append(tempDestroyed, abstract)
@ -175,13 +177,15 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
// Run the graph transforms so we have the information we need to // Run the graph transforms so we have the information we need to
// build references. // build references.
log.Println("[TRACE] DestroyEdgeTransformer: constructing temporary graph for analysis of references")
for _, s := range steps { for _, s := range steps {
log.Printf("[TRACE] DestroyEdgeTransformer: running %T on temporary graph", s)
if err := s.Transform(&tempG); err != nil { if err := s.Transform(&tempG); err != nil {
log.Printf("[TRACE] DestroyEdgeTransformer: %T failed: %s", s, err)
return err return err
} }
} }
log.Printf("[TRACE] DestroyEdgeTransformer: temporary reference graph: %s", tempG.String())
log.Printf("[TRACE] DestroyEdgeTransformer: reference graph: %s", tempG.String())
// Go through all the nodes in the graph and determine what they // Go through all the nodes in the graph and determine what they
// depend on. // depend on.

View File

@ -9,10 +9,11 @@ import (
func TestDestroyEdgeTransformer_basic(t *testing.T) { func TestDestroyEdgeTransformer_basic(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeDestroyerTest{AddrString: "test.A"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A"})
g.Add(&graphNodeDestroyerTest{AddrString: "test.B"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-basic"), Config: testModule(t, "transform-destroy-edge-basic"),
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -21,17 +22,18 @@ func TestDestroyEdgeTransformer_basic(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformDestroyEdgeBasicStr) expected := strings.TrimSpace(testTransformDestroyEdgeBasicStr)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
func TestDestroyEdgeTransformer_create(t *testing.T) { func TestDestroyEdgeTransformer_create(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeDestroyerTest{AddrString: "test.A"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A"})
g.Add(&graphNodeDestroyerTest{AddrString: "test.B"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B"})
g.Add(&graphNodeCreatorTest{AddrString: "test.A"}) g.Add(&graphNodeCreatorTest{AddrString: "test_object.A"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-basic"), Config: testModule(t, "transform-destroy-edge-basic"),
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -40,17 +42,18 @@ func TestDestroyEdgeTransformer_create(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformDestroyEdgeCreatorStr) expected := strings.TrimSpace(testTransformDestroyEdgeCreatorStr)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
func TestDestroyEdgeTransformer_multi(t *testing.T) { func TestDestroyEdgeTransformer_multi(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeDestroyerTest{AddrString: "test.A"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A"})
g.Add(&graphNodeDestroyerTest{AddrString: "test.B"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.B"})
g.Add(&graphNodeDestroyerTest{AddrString: "test.C"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.C"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-multi"), Config: testModule(t, "transform-destroy-edge-multi"),
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -59,15 +62,16 @@ func TestDestroyEdgeTransformer_multi(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformDestroyEdgeMultiStr) expected := strings.TrimSpace(testTransformDestroyEdgeMultiStr)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
func TestDestroyEdgeTransformer_selfRef(t *testing.T) { func TestDestroyEdgeTransformer_selfRef(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeDestroyerTest{AddrString: "test.A"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.A"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-self-ref"), Config: testModule(t, "transform-destroy-edge-self-ref"),
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -76,16 +80,17 @@ func TestDestroyEdgeTransformer_selfRef(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformDestroyEdgeSelfRefStr) expected := strings.TrimSpace(testTransformDestroyEdgeSelfRefStr)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
func TestDestroyEdgeTransformer_module(t *testing.T) { func TestDestroyEdgeTransformer_module(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeDestroyerTest{AddrString: "module.child.aws_instance.b"}) g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.b"})
g.Add(&graphNodeDestroyerTest{AddrString: "aws_instance.a"}) g.Add(&graphNodeDestroyerTest{AddrString: "test_object.a"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-module"), Config: testModule(t, "transform-destroy-edge-module"),
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -94,17 +99,18 @@ func TestDestroyEdgeTransformer_module(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformDestroyEdgeModuleStr) expected := strings.TrimSpace(testTransformDestroyEdgeModuleStr)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
func TestDestroyEdgeTransformer_moduleOnly(t *testing.T) { func TestDestroyEdgeTransformer_moduleOnly(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance} g := Graph{Path: addrs.RootModuleInstance}
g.Add(&graphNodeDestroyerTest{AddrString: "module.child.aws_instance.a"}) g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.a"})
g.Add(&graphNodeDestroyerTest{AddrString: "module.child.aws_instance.b"}) g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.b"})
g.Add(&graphNodeDestroyerTest{AddrString: "module.child.aws_instance.c"}) g.Add(&graphNodeDestroyerTest{AddrString: "module.child.test_object.c"})
tf := &DestroyEdgeTransformer{ tf := &DestroyEdgeTransformer{
Config: testModule(t, "transform-destroy-edge-module-only"), Config: testModule(t, "transform-destroy-edge-module-only"),
Components: simpleMockComponentFactory(),
} }
if err := tf.Transform(&g); err != nil { if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -112,15 +118,15 @@ func TestDestroyEdgeTransformer_moduleOnly(t *testing.T) {
actual := strings.TrimSpace(g.String()) actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(` expected := strings.TrimSpace(`
module.child.aws_instance.a (destroy) module.child.test_object.a (destroy)
module.child.aws_instance.b (destroy) module.child.test_object.b (destroy)
module.child.aws_instance.c (destroy) module.child.test_object.c (destroy)
module.child.aws_instance.b (destroy) module.child.test_object.b (destroy)
module.child.aws_instance.c (destroy) module.child.test_object.c (destroy)
module.child.aws_instance.c (destroy) module.child.test_object.c (destroy)
`) `)
if actual != expected { if actual != expected {
t.Fatalf("bad:\n\n%s", actual) t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
} }
} }
@ -129,17 +135,43 @@ type graphNodeCreatorTest struct {
Refs []string Refs []string
} }
func (n *graphNodeCreatorTest) Name() string { return n.CreateAddr().String() } var (
func (n *graphNodeCreatorTest) CreateAddr() *ResourceAddress { _ GraphNodeCreator = (*graphNodeCreatorTest)(nil)
addr, err := ParseResourceAddress(n.AddrString) _ GraphNodeReferencer = (*graphNodeCreatorTest)(nil)
if err != nil { )
panic(err)
}
func (n *graphNodeCreatorTest) Name() string {
return n.CreateAddr().String()
}
func (n *graphNodeCreatorTest) mustAddr() addrs.AbsResourceInstance {
addr, diags := addrs.ParseAbsResourceInstanceStr(n.AddrString)
if diags.HasErrors() {
panic(diags.Err())
}
return addr return addr
} }
func (n *graphNodeCreatorTest) References() []string { return n.Refs } func (n *graphNodeCreatorTest) Path() addrs.ModuleInstance {
return n.mustAddr().Module
}
func (n *graphNodeCreatorTest) CreateAddr() *addrs.AbsResourceInstance {
addr := n.mustAddr()
return &addr
}
func (n *graphNodeCreatorTest) References() []*addrs.Reference {
ret := make([]*addrs.Reference, len(n.Refs))
for i, str := range n.Refs {
ref, diags := addrs.ParseRefStr(str)
if diags.HasErrors() {
panic(diags.Err())
}
ret[i] = ref
}
return ret
}
type graphNodeDestroyerTest struct { type graphNodeDestroyerTest struct {
AddrString string AddrString string
@ -147,6 +179,8 @@ type graphNodeDestroyerTest struct {
Modified bool Modified bool
} }
var _ GraphNodeDestroyer = (*graphNodeDestroyerTest)(nil)
func (n *graphNodeDestroyerTest) Name() string { func (n *graphNodeDestroyerTest) Name() string {
result := n.DestroyAddr().String() + " (destroy)" result := n.DestroyAddr().String() + " (destroy)"
if n.Modified { if n.Modified {
@ -156,51 +190,57 @@ func (n *graphNodeDestroyerTest) Name() string {
return result return result
} }
func (n *graphNodeDestroyerTest) CreateBeforeDestroy() bool { return n.CBD } func (n *graphNodeDestroyerTest) mustAddr() addrs.AbsResourceInstance {
addr, diags := addrs.ParseAbsResourceInstanceStr(n.AddrString)
if diags.HasErrors() {
panic(diags.Err())
}
return addr
}
func (n *graphNodeDestroyerTest) CreateBeforeDestroy() bool {
return n.CBD
}
func (n *graphNodeDestroyerTest) ModifyCreateBeforeDestroy(v bool) error { func (n *graphNodeDestroyerTest) ModifyCreateBeforeDestroy(v bool) error {
n.Modified = true n.Modified = true
return nil return nil
} }
func (n *graphNodeDestroyerTest) DestroyAddr() *ResourceAddress { func (n *graphNodeDestroyerTest) DestroyAddr() *addrs.AbsResourceInstance {
addr, err := ParseResourceAddress(n.AddrString) addr := n.mustAddr()
if err != nil { return &addr
panic(err)
}
return addr
} }
const testTransformDestroyEdgeBasicStr = ` const testTransformDestroyEdgeBasicStr = `
test.A (destroy) test_object.A (destroy)
test.B (destroy) test_object.B (destroy)
test.B (destroy) test_object.B (destroy)
` `
const testTransformDestroyEdgeCreatorStr = ` const testTransformDestroyEdgeCreatorStr = `
test.A test_object.A
test.A (destroy) test_object.A (destroy)
test.A (destroy) test_object.A (destroy)
test.B (destroy) test_object.B (destroy)
test.B (destroy) test_object.B (destroy)
` `
const testTransformDestroyEdgeMultiStr = ` const testTransformDestroyEdgeMultiStr = `
test.A (destroy) test_object.A (destroy)
test.B (destroy) test_object.B (destroy)
test.C (destroy) test_object.C (destroy)
test.B (destroy) test_object.B (destroy)
test.C (destroy) test_object.C (destroy)
test.C (destroy) test_object.C (destroy)
` `
const testTransformDestroyEdgeSelfRefStr = ` const testTransformDestroyEdgeSelfRefStr = `
test.A (destroy) test_object.A (destroy)
` `
const testTransformDestroyEdgeModuleStr = ` const testTransformDestroyEdgeModuleStr = `
aws_instance.a (destroy) module.child.test_object.b (destroy)
module.child.aws_instance.b (destroy) test_object.a (destroy)
aws_instance.a (destroy) test_object.a (destroy)
` `