diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/transform_diff.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/transform_diff.go | 220 |
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. | ||
22 | type DiffTransformer struct { | 15 | type 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 | ||
30 | func (t *DiffTransformer) Transform(g *Graph) error { | 21 | func (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 | } |