]>
Commit | Line | Data |
---|---|---|
107c1cdb ND |
1 | package plans |
2 | ||
3 | import ( | |
4 | "github.com/hashicorp/terraform/addrs" | |
5 | "github.com/hashicorp/terraform/states" | |
6 | "github.com/zclconf/go-cty/cty" | |
7 | ) | |
8 | ||
9 | // Changes describes various actions that Terraform will attempt to take if | |
10 | // the corresponding plan is applied. | |
11 | // | |
12 | // A Changes object can be rendered into a visual diff (by the caller, using | |
13 | // code in another package) for display to the user. | |
14 | type Changes struct { | |
15 | // Resources tracks planned changes to resource instance objects. | |
16 | Resources []*ResourceInstanceChangeSrc | |
17 | ||
18 | // Outputs tracks planned changes output values. | |
19 | // | |
20 | // Note that although an in-memory plan contains planned changes for | |
21 | // outputs throughout the configuration, a plan serialized | |
22 | // to disk retains only the root outputs because they are | |
23 | // externally-visible, while other outputs are implementation details and | |
24 | // can be easily re-calculated during the apply phase. Therefore only root | |
25 | // module outputs will survive a round-trip through a plan file. | |
26 | Outputs []*OutputChangeSrc | |
27 | } | |
28 | ||
29 | // NewChanges returns a valid Changes object that describes no changes. | |
30 | func NewChanges() *Changes { | |
31 | return &Changes{} | |
32 | } | |
33 | ||
34 | func (c *Changes) Empty() bool { | |
35 | for _, res := range c.Resources { | |
36 | if res.Action != NoOp { | |
37 | return false | |
38 | } | |
39 | } | |
40 | return true | |
41 | } | |
42 | ||
43 | // ResourceInstance returns the planned change for the current object of the | |
44 | // resource instance of the given address, if any. Returns nil if no change is | |
45 | // planned. | |
46 | func (c *Changes) ResourceInstance(addr addrs.AbsResourceInstance) *ResourceInstanceChangeSrc { | |
47 | addrStr := addr.String() | |
48 | for _, rc := range c.Resources { | |
49 | if rc.Addr.String() == addrStr && rc.DeposedKey == states.NotDeposed { | |
50 | return rc | |
51 | } | |
52 | } | |
53 | ||
54 | return nil | |
55 | } | |
56 | ||
57 | // ResourceInstanceDeposed returns the plan change of a deposed object of | |
58 | // the resource instance of the given address, if any. Returns nil if no change | |
59 | // is planned. | |
60 | func (c *Changes) ResourceInstanceDeposed(addr addrs.AbsResourceInstance, key states.DeposedKey) *ResourceInstanceChangeSrc { | |
61 | addrStr := addr.String() | |
62 | for _, rc := range c.Resources { | |
63 | if rc.Addr.String() == addrStr && rc.DeposedKey == key { | |
64 | return rc | |
65 | } | |
66 | } | |
67 | ||
68 | return nil | |
69 | } | |
70 | ||
71 | // OutputValue returns the planned change for the output value with the | |
72 | // given address, if any. Returns nil if no change is planned. | |
73 | func (c *Changes) OutputValue(addr addrs.AbsOutputValue) *OutputChangeSrc { | |
74 | addrStr := addr.String() | |
75 | for _, oc := range c.Outputs { | |
76 | if oc.Addr.String() == addrStr { | |
77 | return oc | |
78 | } | |
79 | } | |
80 | ||
81 | return nil | |
82 | } | |
83 | ||
84 | // SyncWrapper returns a wrapper object around the receiver that can be used | |
85 | // to make certain changes to the receiver in a concurrency-safe way, as long | |
86 | // as all callers share the same wrapper object. | |
87 | func (c *Changes) SyncWrapper() *ChangesSync { | |
88 | return &ChangesSync{ | |
89 | changes: c, | |
90 | } | |
91 | } | |
92 | ||
93 | // ResourceInstanceChange describes a change to a particular resource instance | |
94 | // object. | |
95 | type ResourceInstanceChange struct { | |
96 | // Addr is the absolute address of the resource instance that the change | |
97 | // will apply to. | |
98 | Addr addrs.AbsResourceInstance | |
99 | ||
100 | // DeposedKey is the identifier for a deposed object associated with the | |
101 | // given instance, or states.NotDeposed if this change applies to the | |
102 | // current object. | |
103 | // | |
104 | // A Replace change for a resource with create_before_destroy set will | |
105 | // create a new DeposedKey temporarily during replacement. In that case, | |
106 | // DeposedKey in the plan is always states.NotDeposed, representing that | |
107 | // the current object is being replaced with the deposed. | |
108 | DeposedKey states.DeposedKey | |
109 | ||
110 | // Provider is the address of the provider configuration that was used | |
111 | // to plan this change, and thus the configuration that must also be | |
112 | // used to apply it. | |
113 | ProviderAddr addrs.AbsProviderConfig | |
114 | ||
115 | // Change is an embedded description of the change. | |
116 | Change | |
117 | ||
118 | // RequiredReplace is a set of paths that caused the change action to be | |
119 | // Replace rather than Update. Always nil if the change action is not | |
120 | // Replace. | |
121 | // | |
122 | // This is retained only for UI-plan-rendering purposes and so it does not | |
123 | // currently survive a round-trip through a saved plan file. | |
124 | RequiredReplace cty.PathSet | |
125 | ||
126 | // Private allows a provider to stash any extra data that is opaque to | |
127 | // Terraform that relates to this change. Terraform will save this | |
128 | // byte-for-byte and return it to the provider in the apply call. | |
129 | Private []byte | |
130 | } | |
131 | ||
132 | // Encode produces a variant of the reciever that has its change values | |
133 | // serialized so it can be written to a plan file. Pass the implied type of the | |
134 | // corresponding resource type schema for correct operation. | |
135 | func (rc *ResourceInstanceChange) Encode(ty cty.Type) (*ResourceInstanceChangeSrc, error) { | |
136 | cs, err := rc.Change.Encode(ty) | |
137 | if err != nil { | |
138 | return nil, err | |
139 | } | |
140 | return &ResourceInstanceChangeSrc{ | |
141 | Addr: rc.Addr, | |
142 | DeposedKey: rc.DeposedKey, | |
143 | ProviderAddr: rc.ProviderAddr, | |
144 | ChangeSrc: *cs, | |
145 | RequiredReplace: rc.RequiredReplace, | |
146 | Private: rc.Private, | |
147 | }, err | |
148 | } | |
149 | ||
150 | // Simplify will, where possible, produce a change with a simpler action than | |
151 | // the receiever given a flag indicating whether the caller is dealing with | |
152 | // a normal apply or a destroy. This flag deals with the fact that Terraform | |
153 | // Core uses a specialized graph node type for destroying; only that | |
154 | // specialized node should set "destroying" to true. | |
155 | // | |
156 | // The following table shows the simplification behavior: | |
157 | // | |
158 | // Action Destroying? New Action | |
159 | // --------+-------------+----------- | |
160 | // Create true NoOp | |
161 | // Delete false NoOp | |
162 | // Replace true Delete | |
163 | // Replace false Create | |
164 | // | |
165 | // For any combination not in the above table, the Simplify just returns the | |
166 | // receiver as-is. | |
167 | func (rc *ResourceInstanceChange) Simplify(destroying bool) *ResourceInstanceChange { | |
168 | if destroying { | |
169 | switch rc.Action { | |
170 | case Delete: | |
171 | // We'll fall out and just return rc verbatim, then. | |
172 | case CreateThenDelete, DeleteThenCreate: | |
173 | return &ResourceInstanceChange{ | |
174 | Addr: rc.Addr, | |
175 | DeposedKey: rc.DeposedKey, | |
176 | Private: rc.Private, | |
177 | ProviderAddr: rc.ProviderAddr, | |
178 | Change: Change{ | |
179 | Action: Delete, | |
180 | Before: rc.Before, | |
181 | After: cty.NullVal(rc.Before.Type()), | |
182 | }, | |
183 | } | |
184 | default: | |
185 | return &ResourceInstanceChange{ | |
186 | Addr: rc.Addr, | |
187 | DeposedKey: rc.DeposedKey, | |
188 | Private: rc.Private, | |
189 | ProviderAddr: rc.ProviderAddr, | |
190 | Change: Change{ | |
191 | Action: NoOp, | |
192 | Before: rc.Before, | |
193 | After: rc.Before, | |
194 | }, | |
195 | } | |
196 | } | |
197 | } else { | |
198 | switch rc.Action { | |
199 | case Delete: | |
200 | return &ResourceInstanceChange{ | |
201 | Addr: rc.Addr, | |
202 | DeposedKey: rc.DeposedKey, | |
203 | Private: rc.Private, | |
204 | ProviderAddr: rc.ProviderAddr, | |
205 | Change: Change{ | |
206 | Action: NoOp, | |
207 | Before: rc.Before, | |
208 | After: rc.Before, | |
209 | }, | |
210 | } | |
211 | case CreateThenDelete, DeleteThenCreate: | |
212 | return &ResourceInstanceChange{ | |
213 | Addr: rc.Addr, | |
214 | DeposedKey: rc.DeposedKey, | |
215 | Private: rc.Private, | |
216 | ProviderAddr: rc.ProviderAddr, | |
217 | Change: Change{ | |
218 | Action: Create, | |
219 | Before: cty.NullVal(rc.After.Type()), | |
220 | After: rc.After, | |
221 | }, | |
222 | } | |
223 | } | |
224 | } | |
225 | ||
226 | // If we fall out here then our change is already simple enough. | |
227 | return rc | |
228 | } | |
229 | ||
230 | // OutputChange describes a change to an output value. | |
231 | type OutputChange struct { | |
232 | // Addr is the absolute address of the output value that the change | |
233 | // will apply to. | |
234 | Addr addrs.AbsOutputValue | |
235 | ||
236 | // Change is an embedded description of the change. | |
237 | // | |
238 | // For output value changes, the type constraint for the DynamicValue | |
239 | // instances is always cty.DynamicPseudoType. | |
240 | Change | |
241 | ||
242 | // Sensitive, if true, indicates that either the old or new value in the | |
243 | // change is sensitive and so a rendered version of the plan in the UI | |
244 | // should elide the actual values while still indicating the action of the | |
245 | // change. | |
246 | Sensitive bool | |
247 | } | |
248 | ||
249 | // Encode produces a variant of the reciever that has its change values | |
250 | // serialized so it can be written to a plan file. | |
251 | func (oc *OutputChange) Encode() (*OutputChangeSrc, error) { | |
252 | cs, err := oc.Change.Encode(cty.DynamicPseudoType) | |
253 | if err != nil { | |
254 | return nil, err | |
255 | } | |
256 | return &OutputChangeSrc{ | |
257 | Addr: oc.Addr, | |
258 | ChangeSrc: *cs, | |
259 | Sensitive: oc.Sensitive, | |
260 | }, err | |
261 | } | |
262 | ||
263 | // Change describes a single change with a given action. | |
264 | type Change struct { | |
265 | // Action defines what kind of change is being made. | |
266 | Action Action | |
267 | ||
268 | // Interpretation of Before and After depend on Action: | |
269 | // | |
270 | // NoOp Before and After are the same, unchanged value | |
271 | // Create Before is nil, and After is the expected value after create. | |
272 | // Read Before is any prior value (nil if no prior), and After is the | |
273 | // value that was or will be read. | |
274 | // Update Before is the value prior to update, and After is the expected | |
275 | // value after update. | |
276 | // Replace As with Update. | |
277 | // Delete Before is the value prior to delete, and After is always nil. | |
278 | // | |
279 | // Unknown values may appear anywhere within the Before and After values, | |
280 | // either as the values themselves or as nested elements within known | |
281 | // collections/structures. | |
282 | Before, After cty.Value | |
283 | } | |
284 | ||
285 | // Encode produces a variant of the reciever that has its change values | |
286 | // serialized so it can be written to a plan file. Pass the type constraint | |
287 | // that the values are expected to conform to; to properly decode the values | |
288 | // later an identical type constraint must be provided at that time. | |
289 | // | |
290 | // Where a Change is embedded in some other struct, it's generally better | |
291 | // to call the corresponding Encode method of that struct rather than working | |
292 | // directly with its embedded Change. | |
293 | func (c *Change) Encode(ty cty.Type) (*ChangeSrc, error) { | |
294 | beforeDV, err := NewDynamicValue(c.Before, ty) | |
295 | if err != nil { | |
296 | return nil, err | |
297 | } | |
298 | afterDV, err := NewDynamicValue(c.After, ty) | |
299 | if err != nil { | |
300 | return nil, err | |
301 | } | |
302 | ||
303 | return &ChangeSrc{ | |
304 | Action: c.Action, | |
305 | Before: beforeDV, | |
306 | After: afterDV, | |
307 | }, nil | |
308 | } |