aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/terraform/transform_diff.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/transform_diff.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/terraform/transform_diff.go220
1 files changed, 163 insertions, 57 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_diff.go b/vendor/github.com/hashicorp/terraform/terraform/transform_diff.go
index ad46d3c..6fb915f 100644
--- a/vendor/github.com/hashicorp/terraform/terraform/transform_diff.go
+++ b/vendor/github.com/hashicorp/terraform/terraform/transform_diff.go
@@ -4,83 +4,189 @@ import (
4 "fmt" 4 "fmt"
5 "log" 5 "log"
6 6
7 "github.com/hashicorp/terraform/config/module"
8 "github.com/hashicorp/terraform/dag" 7 "github.com/hashicorp/terraform/dag"
8 "github.com/hashicorp/terraform/plans"
9 "github.com/hashicorp/terraform/states"
10 "github.com/hashicorp/terraform/tfdiags"
9) 11)
10 12
11// DiffTransformer is a GraphTransformer that adds the elements of 13// DiffTransformer is a GraphTransformer that adds graph nodes representing
12// the diff to the graph. 14// each of the resource changes described in the given Changes object.
13//
14// This transform is used for example by the ApplyGraphBuilder to ensure
15// that only resources that are being modified are represented in the graph.
16//
17// Module and State is still required for the DiffTransformer for annotations
18// since the Diff doesn't contain all the information required to build the
19// complete graph (such as create-before-destroy information). The graph
20// is built based on the diff first, though, ensuring that only resources
21// that are being modified are present in the graph.
22type DiffTransformer struct { 15type DiffTransformer struct {
23 Concrete ConcreteResourceNodeFunc 16 Concrete ConcreteResourceInstanceNodeFunc
24 17 State *states.State
25 Diff *Diff 18 Changes *plans.Changes
26 Module *module.Tree
27 State *State
28} 19}
29 20
30func (t *DiffTransformer) Transform(g *Graph) error { 21func (t *DiffTransformer) Transform(g *Graph) error {
31 // If the diff is nil or empty (nil is empty) then do nothing 22 if t.Changes == nil || len(t.Changes.Resources) == 0 {
32 if t.Diff.Empty() { 23 // Nothing to do!
33 return nil 24 return nil
34 } 25 }
35 26
36 // Go through all the modules in the diff. 27 // Go through all the modules in the diff.
37 log.Printf("[TRACE] DiffTransformer: starting") 28 log.Printf("[TRACE] DiffTransformer starting")
38 var nodes []dag.Vertex 29
39 for _, m := range t.Diff.Modules { 30 var diags tfdiags.Diagnostics
40 log.Printf("[TRACE] DiffTransformer: Module: %s", m) 31 state := t.State
41 // TODO: If this is a destroy diff then add a module destroy node 32 changes := t.Changes
42 33
43 // Go through all the resources in this module. 34 // DiffTransformer creates resource _instance_ nodes. If there are any
44 for name, inst := range m.Resources { 35 // whole-resource nodes already in the graph, we must ensure that they
45 log.Printf("[TRACE] DiffTransformer: Resource %q: %#v", name, inst) 36 // get evaluated before any of the corresponding instances by creating
46 37 // dependency edges, so we'll do some prep work here to ensure we'll only
47 // We have changes! This is a create or update operation. 38 // create connections to nodes that existed before we started here.
48 // First grab the address so we have a unique way to 39 resourceNodes := map[string][]GraphNodeResource{}
49 // reference this resource. 40 for _, node := range g.Vertices() {
50 addr, err := parseResourceAddressInternal(name) 41 rn, ok := node.(GraphNodeResource)
51 if err != nil { 42 if !ok {
52 panic(fmt.Sprintf( 43 continue
53 "Error parsing internal name, this is a bug: %q", name)) 44 }
54 } 45 // We ignore any instances that _also_ implement
46 // GraphNodeResourceInstance, since in the unlikely event that they
47 // do exist we'd probably end up creating cycles by connecting them.
48 if _, ok := node.(GraphNodeResourceInstance); ok {
49 continue
50 }
51
52 addr := rn.ResourceAddr().String()
53 resourceNodes[addr] = append(resourceNodes[addr], rn)
54 }
55
56 for _, rc := range changes.Resources {
57 addr := rc.Addr
58 dk := rc.DeposedKey
59
60 log.Printf("[TRACE] DiffTransformer: found %s change for %s %s", rc.Action, addr, dk)
61
62 // Depending on the action we'll need some different combinations of
63 // nodes, because destroying uses a special node type separate from
64 // other actions.
65 var update, delete, createBeforeDestroy bool
66 switch rc.Action {
67 case plans.NoOp:
68 continue
69 case plans.Delete:
70 delete = true
71 case plans.DeleteThenCreate, plans.CreateThenDelete:
72 update = true
73 delete = true
74 createBeforeDestroy = (rc.Action == plans.CreateThenDelete)
75 default:
76 update = true
77 }
78
79 if dk != states.NotDeposed && update {
80 diags = diags.Append(tfdiags.Sourceless(
81 tfdiags.Error,
82 "Invalid planned change for deposed object",
83 fmt.Sprintf("The plan contains a non-delete change for %s deposed object %s. The only valid action for a deposed object is to destroy it, so this is a bug in Terraform.", addr, dk),
84 ))
85 continue
86 }
55 87
56 // Very important: add the module path for this resource to 88 // If we're going to do a create_before_destroy Replace operation then
57 // the address. Remove "root" from it. 89 // we need to allocate a DeposedKey to use to retain the
58 addr.Path = m.Path[1:] 90 // not-yet-destroyed prior object, so that the delete node can destroy
91 // _that_ rather than the newly-created node, which will be current
92 // by the time the delete node is visited.
93 if update && delete && createBeforeDestroy {
94 // In this case, variable dk will be the _pre-assigned_ DeposedKey
95 // that must be used if the update graph node deposes the current
96 // instance, which will then align with the same key we pass
97 // into the destroy node to ensure we destroy exactly the deposed
98 // object we expect.
99 if state != nil {
100 ris := state.ResourceInstance(addr)
101 if ris == nil {
102 // Should never happen, since we don't plan to replace an
103 // instance that doesn't exist yet.
104 diags = diags.Append(tfdiags.Sourceless(
105 tfdiags.Error,
106 "Invalid planned change",
107 fmt.Sprintf("The plan contains a replace change for %s, which doesn't exist yet. This is a bug in Terraform.", addr),
108 ))
109 continue
110 }
111
112 // Allocating a deposed key separately from using it can be racy
113 // in general, but we assume here that nothing except the apply
114 // node we instantiate below will actually make new deposed objects
115 // in practice, and so the set of already-used keys will not change
116 // between now and then.
117 dk = ris.FindUnusedDeposedKey()
118 } else {
119 // If we have no state at all yet then we can use _any_
120 // DeposedKey.
121 dk = states.NewDeposedKey()
122 }
123 }
59 124
60 // If we're destroying, add the destroy node 125 if update {
61 if inst.Destroy || inst.GetDestroyDeposed() { 126 // All actions except destroying the node type chosen by t.Concrete
62 abstract := &NodeAbstractResource{Addr: addr} 127 abstract := NewNodeAbstractResourceInstance(addr)
63 g.Add(&NodeDestroyResource{NodeAbstractResource: abstract}) 128 var node dag.Vertex = abstract
129 if f := t.Concrete; f != nil {
130 node = f(abstract)
64 } 131 }
65 132
66 // If we have changes, then add the applyable version 133 if createBeforeDestroy {
67 if len(inst.Attributes) > 0 { 134 // We'll attach our pre-allocated DeposedKey to the node if
68 // Add the resource to the graph 135 // it supports that. NodeApplyableResourceInstance is the
69 abstract := &NodeAbstractResource{Addr: addr} 136 // specific concrete node type we are looking for here really,
70 var node dag.Vertex = abstract 137 // since that's the only node type that might depose objects.
71 if f := t.Concrete; f != nil { 138 if dn, ok := node.(GraphNodeDeposer); ok {
72 node = f(abstract) 139 dn.SetPreallocatedDeposedKey(dk)
73 } 140 }
141 log.Printf("[TRACE] DiffTransformer: %s will be represented by %s, deposing prior object to %s", addr, dag.VertexName(node), dk)
142 } else {
143 log.Printf("[TRACE] DiffTransformer: %s will be represented by %s", addr, dag.VertexName(node))
144 }
74 145
75 nodes = append(nodes, node) 146 g.Add(node)
147 rsrcAddr := addr.ContainingResource().String()
148 for _, rsrcNode := range resourceNodes[rsrcAddr] {
149 g.Connect(dag.BasicEdge(node, rsrcNode))
150 }
151 }
152
153 if delete {
154 // Destroying always uses a destroy-specific node type, though
155 // which one depends on whether we're destroying a current object
156 // or a deposed object.
157 var node GraphNodeResourceInstance
158 abstract := NewNodeAbstractResourceInstance(addr)
159 if dk == states.NotDeposed {
160 node = &NodeDestroyResourceInstance{
161 NodeAbstractResourceInstance: abstract,
162 DeposedKey: dk,
163 }
164 node.(*NodeDestroyResourceInstance).ModifyCreateBeforeDestroy(createBeforeDestroy)
165 } else {
166 node = &NodeDestroyDeposedResourceInstanceObject{
167 NodeAbstractResourceInstance: abstract,
168 DeposedKey: dk,
169 }
170 }
171 if dk == states.NotDeposed {
172 log.Printf("[TRACE] DiffTransformer: %s will be represented for destruction by %s", addr, dag.VertexName(node))
173 } else {
174 log.Printf("[TRACE] DiffTransformer: %s deposed object %s will be represented for destruction by %s", addr, dk, dag.VertexName(node))
175 }
176 g.Add(node)
177 rsrcAddr := addr.ContainingResource().String()
178 for _, rsrcNode := range resourceNodes[rsrcAddr] {
179 // We connect this edge "forwards" (even though destroy dependencies
180 // are often inverted) because evaluating the resource node
181 // after the destroy node could cause an unnecessary husk of
182 // a resource state to be re-added.
183 g.Connect(dag.BasicEdge(node, rsrcNode))
76 } 184 }
77 } 185 }
78 }
79 186
80 // Add all the nodes to the graph
81 for _, n := range nodes {
82 g.Add(n)
83 } 187 }
84 188
85 return nil 189 log.Printf("[TRACE] DiffTransformer complete")
190
191 return diags.Err()
86} 192}