1package terraform
2
3import (
4	"log"
5
6	"github.com/hashicorp/terraform/internal/addrs"
7	"github.com/hashicorp/terraform/internal/states"
8
9	"github.com/hashicorp/terraform/internal/configs"
10	"github.com/hashicorp/terraform/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.
58	destroyers := make(map[string][]GraphNodeDestroyer)
59
60	// Record the creators, which will need to depend on the destroyers if they
61	// are only being updated.
62	creators := make(map[string][]GraphNodeCreator)
63
64	// destroyersByResource records each destroyer by the ConfigResource
65	// address.  We use this because dependencies are only referenced as
66	// resources and have no index or module instance information, but we will
67	// want to connect all the individual instances for correct ordering.
68	destroyersByResource := make(map[string][]GraphNodeDestroyer)
69	for _, v := range g.Vertices() {
70		switch n := v.(type) {
71		case GraphNodeDestroyer:
72			addrP := n.DestroyAddr()
73			if addrP == nil {
74				log.Printf("[WARN] DestroyEdgeTransformer: %q (%T) has no destroy address", dag.VertexName(n), v)
75				continue
76			}
77			addr := *addrP
78
79			key := addr.String()
80			log.Printf("[TRACE] DestroyEdgeTransformer: %q (%T) destroys %s", dag.VertexName(n), v, key)
81			destroyers[key] = append(destroyers[key], n)
82
83			resAddr := addr.ContainingResource().Config().String()
84			destroyersByResource[resAddr] = append(destroyersByResource[resAddr], n)
85		case GraphNodeCreator:
86			addr := n.CreateAddr().ContainingResource().Config().String()
87			creators[addr] = append(creators[addr], n)
88		}
89	}
90
91	// If we aren't destroying anything, there will be no edges to make
92	// so just exit early and avoid future work.
93	if len(destroyers) == 0 {
94		return nil
95	}
96
97	// Connect destroy despendencies as stored in the state
98	for _, ds := range destroyers {
99		for _, des := range ds {
100			ri, ok := des.(GraphNodeResourceInstance)
101			if !ok {
102				continue
103			}
104
105			for _, resAddr := range ri.StateDependencies() {
106				for _, desDep := range destroyersByResource[resAddr.String()] {
107					if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(desDep, des) {
108						log.Printf("[TRACE] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(desDep), dag.VertexName(des))
109						g.Connect(dag.BasicEdge(desDep, des))
110					} else {
111						log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(desDep), dag.VertexName(des))
112					}
113				}
114
115				// We can have some create or update nodes which were
116				// dependents of the destroy node. If they have no destroyer
117				// themselves, make the connection directly from the creator.
118				for _, createDep := range creators[resAddr.String()] {
119					if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(createDep, des) {
120						log.Printf("[DEBUG] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(createDep), dag.VertexName(des))
121						g.Connect(dag.BasicEdge(createDep, des))
122					} else {
123						log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(createDep), dag.VertexName(des))
124					}
125				}
126			}
127		}
128	}
129
130	// connect creators to any destroyers on which they may depend
131	for _, cs := range creators {
132		for _, c := range cs {
133			ri, ok := c.(GraphNodeResourceInstance)
134			if !ok {
135				continue
136			}
137
138			for _, resAddr := range ri.StateDependencies() {
139				for _, desDep := range destroyersByResource[resAddr.String()] {
140					if !graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(c, desDep) {
141						log.Printf("[TRACE] DestroyEdgeTransformer: %s has stored dependency of %s\n", dag.VertexName(c), dag.VertexName(desDep))
142						g.Connect(dag.BasicEdge(c, desDep))
143					} else {
144						log.Printf("[TRACE] DestroyEdgeTransformer: skipping %s => %s inter-module-instance dependency\n", dag.VertexName(c), dag.VertexName(desDep))
145					}
146				}
147			}
148		}
149	}
150
151	// Go through and connect creators to destroyers. Going along with
152	// our example, this makes: A_d => A
153	for _, v := range g.Vertices() {
154		cn, ok := v.(GraphNodeCreator)
155		if !ok {
156			continue
157		}
158
159		addr := cn.CreateAddr()
160		if addr == nil {
161			continue
162		}
163
164		for _, d := range destroyers[addr.String()] {
165			// For illustrating our example
166			a_d := d.(dag.Vertex)
167			a := v
168
169			log.Printf(
170				"[TRACE] DestroyEdgeTransformer: connecting creator %q with destroyer %q",
171				dag.VertexName(a), dag.VertexName(a_d))
172
173			g.Connect(dag.BasicEdge(a, a_d))
174		}
175	}
176
177	return nil
178}
179
180// Remove any nodes that aren't needed when destroying modules.
181// Variables, outputs, locals, and expanders may not be able to evaluate
182// correctly, so we can remove these if nothing depends on them. The module
183// closers also need to disable their use of expansion if the module itself is
184// no longer present.
185type pruneUnusedNodesTransformer struct {
186}
187
188func (t *pruneUnusedNodesTransformer) Transform(g *Graph) error {
189	// We need a reverse depth first walk of modules, processing them in order
190	// from the leaf modules to the root. This allows us to remove unneeded
191	// dependencies from child modules, freeing up nodes in the parent module
192	// to also be removed.
193
194	nodes := g.Vertices()
195
196	for removed := true; removed; {
197		removed = false
198
199		for i := 0; i < len(nodes); i++ {
200			// run this in a closure, so we can return early rather than
201			// dealing with complex looping and labels
202			func() {
203				n := nodes[i]
204				switch n := n.(type) {
205				case graphNodeTemporaryValue:
206					// root module outputs indicate they are not temporary by
207					// returning false here.
208					if !n.temporaryValue() {
209						return
210					}
211
212					// temporary values, which consist of variables, locals,
213					// and outputs, must be kept if anything refers to them.
214					for _, v := range g.UpEdges(n) {
215						// keep any value which is connected through a
216						// reference
217						if _, ok := v.(GraphNodeReferencer); ok {
218							return
219						}
220					}
221
222				case graphNodeExpandsInstances:
223					// Any nodes that expand instances are kept when their
224					// instances may need to be evaluated.
225					for _, v := range g.UpEdges(n) {
226						switch v.(type) {
227						case graphNodeExpandsInstances:
228							// expanders can always depend on module expansion
229							// themselves
230							return
231						case GraphNodeResourceInstance:
232							// resource instances always depend on their
233							// resource node, which is an expander
234							return
235						}
236					}
237
238				case GraphNodeProvider:
239					// Providers that may have been required by expansion nodes
240					// that we no longer need can also be removed.
241					if g.UpEdges(n).Len() > 0 {
242						return
243					}
244
245				default:
246					return
247				}
248
249				log.Printf("[DEBUG] pruneUnusedNodes: %s is no longer needed, removing", dag.VertexName(n))
250				g.Remove(n)
251				removed = true
252
253				// remove the node from our iteration as well
254				last := len(nodes) - 1
255				nodes[i], nodes[last] = nodes[last], nodes[i]
256				nodes = nodes[:last]
257			}()
258		}
259	}
260
261	return nil
262}
263