1package terraform
2
3import (
4	"log"
5
6	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
7	"github.com/hashicorp/terraform-plugin-sdk/internal/states"
8
9	"github.com/hashicorp/terraform-plugin-sdk/internal/configs"
10	"github.com/hashicorp/terraform-plugin-sdk/internal/dag"
11)
12
13// GraphNodeDestroyer must be implemented by nodes that destroy resources.
14type GraphNodeDestroyer interface {
15	dag.Vertex
16
17	// DestroyAddr is the address of the resource that is being
18	// destroyed by this node. If this returns nil, then this node
19	// is not destroying anything.
20	DestroyAddr() *addrs.AbsResourceInstance
21}
22
23// GraphNodeCreator must be implemented by nodes that create OR update resources.
24type GraphNodeCreator interface {
25	// CreateAddr is the address of the resource being created or updated
26	CreateAddr() *addrs.AbsResourceInstance
27}
28
29// DestroyEdgeTransformer is a GraphTransformer that creates the proper
30// references for destroy resources. Destroy resources are more complex
31// in that they must be depend on the destruction of resources that
32// in turn depend on the CREATION of the node being destroy.
33//
34// That is complicated. Visually:
35//
36//   B_d -> A_d -> A -> B
37//
38// Notice that A destroy depends on B destroy, while B create depends on
39// A create. They're inverted. This must be done for example because often
40// dependent resources will block parent resources from deleting. Concrete
41// example: VPC with subnets, the VPC can't be deleted while there are
42// still subnets.
43type DestroyEdgeTransformer struct {
44	// These are needed to properly build the graph of dependencies
45	// to determine what a destroy node depends on. Any of these can be nil.
46	Config *configs.Config
47	State  *states.State
48
49	// If configuration is present then Schemas is required in order to
50	// obtain schema information from providers and provisioners in order
51	// to properly resolve implicit dependencies.
52	Schemas *Schemas
53}
54
55func (t *DestroyEdgeTransformer) Transform(g *Graph) error {
56	// Build a map of what is being destroyed (by address string) to
57	// the list of destroyers. Usually there will be at most one destroyer
58	// per node, but we allow multiple if present for completeness.
59	destroyers := make(map[string][]GraphNodeDestroyer)
60	destroyerAddrs := make(map[string]addrs.AbsResourceInstance)
61	for _, v := range g.Vertices() {
62		dn, ok := v.(GraphNodeDestroyer)
63		if !ok {
64			continue
65		}
66
67		addrP := dn.DestroyAddr()
68		if addrP == nil {
69			continue
70		}
71		addr := *addrP
72
73		key := addr.String()
74		log.Printf("[TRACE] DestroyEdgeTransformer: %q (%T) destroys %s", dag.VertexName(dn), v, key)
75		destroyers[key] = append(destroyers[key], dn)
76		destroyerAddrs[key] = addr
77	}
78
79	// If we aren't destroying anything, there will be no edges to make
80	// so just exit early and avoid future work.
81	if len(destroyers) == 0 {
82		return nil
83	}
84
85	// Go through and connect creators to destroyers. Going along with
86	// our example, this makes: A_d => A
87	for _, v := range g.Vertices() {
88		cn, ok := v.(GraphNodeCreator)
89		if !ok {
90			continue
91		}
92
93		addr := cn.CreateAddr()
94		if addr == nil {
95			continue
96		}
97
98		key := addr.String()
99		ds := destroyers[key]
100		if len(ds) == 0 {
101			continue
102		}
103
104		for _, d := range ds {
105			// For illustrating our example
106			a_d := d.(dag.Vertex)
107			a := v
108
109			log.Printf(
110				"[TRACE] DestroyEdgeTransformer: connecting creator %q with destroyer %q",
111				dag.VertexName(a), dag.VertexName(a_d))
112
113			g.Connect(&DestroyEdge{S: a, T: a_d})
114
115			// Attach the destroy node to the creator
116			// There really shouldn't be more than one destroyer, but even if
117			// there are, any of them will represent the correct
118			// CreateBeforeDestroy status.
119			if n, ok := cn.(GraphNodeAttachDestroyer); ok {
120				if d, ok := d.(GraphNodeDestroyerCBD); ok {
121					n.AttachDestroyNode(d)
122				}
123			}
124		}
125	}
126
127	// This is strange but is the easiest way to get the dependencies
128	// of a node that is being destroyed. We use another graph to make sure
129	// the resource is in the graph and ask for references. We have to do this
130	// because the node that is being destroyed may NOT be in the graph.
131	//
132	// Example: resource A is force new, then destroy A AND create A are
133	// in the graph. BUT if resource A is just pure destroy, then only
134	// destroy A is in the graph, and create A is not.
135	providerFn := func(a *NodeAbstractProvider) dag.Vertex {
136		return &NodeApplyableProvider{NodeAbstractProvider: a}
137	}
138	steps := []GraphTransformer{
139		// Add the local values
140		&LocalTransformer{Config: t.Config},
141
142		// Add outputs and metadata
143		&OutputTransformer{Config: t.Config},
144		&AttachResourceConfigTransformer{Config: t.Config},
145		&AttachStateTransformer{State: t.State},
146
147		// Add all the variables. We can depend on resources through
148		// variables due to module parameters, and we need to properly
149		// determine that.
150		&RootVariableTransformer{Config: t.Config},
151		&ModuleVariableTransformer{Config: t.Config},
152
153		TransformProviders(nil, providerFn, t.Config),
154
155		// Must attach schemas before ReferenceTransformer so that we can
156		// analyze the configuration to find references.
157		&AttachSchemaTransformer{Schemas: t.Schemas},
158
159		&ReferenceTransformer{},
160	}
161
162	// Go through all the nodes being destroyed and create a graph.
163	// The resulting graph is only of things being CREATED. For example,
164	// following our example, the resulting graph would be:
165	//
166	//   A, B (with no edges)
167	//
168	var tempG Graph
169	var tempDestroyed []dag.Vertex
170	for d := range destroyers {
171		// d is the string key for the resource being destroyed. We actually
172		// want the address value, which we stashed earlier.
173		addr := destroyerAddrs[d]
174
175		// This part is a little bit weird but is the best way to
176		// find the dependencies we need to: build a graph and use the
177		// attach config and state transformers then ask for references.
178		abstract := NewNodeAbstractResourceInstance(addr)
179		tempG.Add(abstract)
180		tempDestroyed = append(tempDestroyed, abstract)
181
182		// We also add the destroy version here since the destroy can
183		// depend on things that the creation doesn't (destroy provisioners).
184		destroy := &NodeDestroyResourceInstance{NodeAbstractResourceInstance: abstract}
185		tempG.Add(destroy)
186		tempDestroyed = append(tempDestroyed, destroy)
187	}
188
189	// Run the graph transforms so we have the information we need to
190	// build references.
191	log.Printf("[TRACE] DestroyEdgeTransformer: constructing temporary graph for analysis of references, starting from:\n%s", tempG.StringWithNodeTypes())
192	for _, s := range steps {
193		log.Printf("[TRACE] DestroyEdgeTransformer: running %T on temporary graph", s)
194		if err := s.Transform(&tempG); err != nil {
195			log.Printf("[TRACE] DestroyEdgeTransformer: %T failed: %s", s, err)
196			return err
197		}
198	}
199	log.Printf("[TRACE] DestroyEdgeTransformer: temporary reference graph:\n%s", tempG.String())
200
201	// Go through all the nodes in the graph and determine what they
202	// depend on.
203	for _, v := range tempDestroyed {
204		// Find all ancestors of this to determine the edges we'll depend on
205		vs, err := tempG.Ancestors(v)
206		if err != nil {
207			return err
208		}
209
210		refs := make([]dag.Vertex, 0, vs.Len())
211		for _, raw := range vs.List() {
212			refs = append(refs, raw.(dag.Vertex))
213		}
214
215		refNames := make([]string, len(refs))
216		for i, ref := range refs {
217			refNames[i] = dag.VertexName(ref)
218		}
219		log.Printf(
220			"[TRACE] DestroyEdgeTransformer: creation node %q references %s",
221			dag.VertexName(v), refNames)
222
223		// If we have no references, then we won't need to do anything
224		if len(refs) == 0 {
225			continue
226		}
227
228		// Get the destroy node for this. In the example of our struct,
229		// we are currently at B and we're looking for B_d.
230		rn, ok := v.(GraphNodeResourceInstance)
231		if !ok {
232			log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s, since it's not a resource", dag.VertexName(v))
233			continue
234		}
235
236		addr := rn.ResourceInstanceAddr()
237		dns := destroyers[addr.String()]
238
239		// We have dependencies, check if any are being destroyed
240		// to build the list of things that we must depend on!
241		//
242		// In the example of the struct, if we have:
243		//
244		//   B_d => A_d => A => B
245		//
246		// Then at this point in the algorithm we started with B_d,
247		// we built B (to get dependencies), and we found A. We're now looking
248		// to see if A_d exists.
249		var depDestroyers []dag.Vertex
250		for _, v := range refs {
251			rn, ok := v.(GraphNodeResourceInstance)
252			if !ok {
253				continue
254			}
255
256			addr := rn.ResourceInstanceAddr()
257			key := addr.String()
258			if ds, ok := destroyers[key]; ok {
259				for _, d := range ds {
260					depDestroyers = append(depDestroyers, d.(dag.Vertex))
261					log.Printf(
262						"[TRACE] DestroyEdgeTransformer: destruction of %q depends on %s",
263						key, dag.VertexName(d))
264				}
265			}
266		}
267
268		// Go through and make the connections. Use the variable
269		// names "a_d" and "b_d" to reference our example.
270		for _, a_d := range dns {
271			for _, b_d := range depDestroyers {
272				if b_d != a_d {
273					log.Printf("[TRACE] DestroyEdgeTransformer: %q depends on %q", dag.VertexName(b_d), dag.VertexName(a_d))
274					g.Connect(dag.BasicEdge(b_d, a_d))
275				}
276			}
277		}
278	}
279
280	return nil
281}
282