-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
graph_builder_plan.go
305 lines (250 loc) · 9.43 KB
/
graph_builder_plan.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
package terraform
import (
"log"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/dag"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// PlanGraphBuilder is a GraphBuilder implementation that builds a graph for
// planning and for other "plan-like" operations which don't require an
// already-calculated plan as input.
//
// Unlike the apply graph builder, this graph builder:
//
// - Makes its decisions primarily based on the given configuration, which
// represents the desired state.
//
// - Ignores certain lifecycle concerns like create_before_destroy, because
// those are only important once we already know what action we're planning
// to take against a particular resource instance.
type PlanGraphBuilder struct {
// Config is the configuration tree to build a plan from.
Config *configs.Config
// State is the current state
State *states.State
// RootVariableValues are the raw input values for root input variables
// given by the caller, which we'll resolve into final values as part
// of the plan walk.
RootVariableValues InputValues
// Plugins is a library of plug-in components (providers and
// provisioners) available for use.
Plugins *contextPlugins
// Targets are resources to target
Targets []addrs.Targetable
// ForceReplace are resource instances where if we would normally have
// generated a NoOp or Update action then we'll force generating a replace
// action instead. Create and Delete actions are not affected.
ForceReplace []addrs.AbsResourceInstance
// skipRefresh indicates that we should skip refreshing managed resources
skipRefresh bool
// preDestroyRefresh indicates that we are executing the refresh which
// happens immediately before a destroy plan, which happens to use the
// normal planing mode so skipPlanChanges cannot be set.
preDestroyRefresh bool
// skipPlanChanges indicates that we should skip the step of comparing
// prior state with configuration and generating planned changes to
// resource instances. (This is for the "refresh only" planning mode,
// where we _only_ do the refresh step.)
skipPlanChanges bool
ConcreteProvider ConcreteProviderNodeFunc
ConcreteResource ConcreteResourceNodeFunc
ConcreteResourceInstance ConcreteResourceInstanceNodeFunc
ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc
ConcreteResourceInstanceDeposed ConcreteResourceInstanceDeposedNodeFunc
ConcreteModule ConcreteModuleNodeFunc
// Plan Operation this graph will be used for.
Operation walkOperation
// ImportTargets are the list of resources to import.
ImportTargets []*ImportTarget
}
// See GraphBuilder
func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) {
log.Printf("[TRACE] building graph for %s", b.Operation)
return (&BasicGraphBuilder{
Steps: b.Steps(),
Name: "PlanGraphBuilder",
}).Build(path)
}
// See GraphBuilder
func (b *PlanGraphBuilder) Steps() []GraphTransformer {
switch b.Operation {
case walkPlan:
b.initPlan()
case walkPlanDestroy:
b.initDestroy()
case walkValidate:
b.initValidate()
case walkImport:
b.initImport()
default:
panic("invalid plan operation: " + b.Operation.String())
}
steps := []GraphTransformer{
// Creates all the resources represented in the config
&ConfigTransformer{
Concrete: b.ConcreteResource,
Config: b.Config,
// Resources are not added from the config on destroy.
skip: b.Operation == walkPlanDestroy,
importTargets: b.ImportTargets,
},
// Add dynamic values
&RootVariableTransformer{Config: b.Config, RawValues: b.RootVariableValues},
&ModuleVariableTransformer{Config: b.Config},
&LocalTransformer{Config: b.Config},
&OutputTransformer{
Config: b.Config,
RefreshOnly: b.skipPlanChanges || b.preDestroyRefresh,
PlanDestroy: b.Operation == walkPlanDestroy,
// NOTE: We currently treat anything built with the plan graph
// builder as "planning" for our purposes here, because we share
// the same graph node implementation between all of the walk
// types and so the pre-planning walks still think they are
// producing a plan even though we immediately discard it.
Planning: true,
},
// Add orphan resources
&OrphanResourceInstanceTransformer{
Concrete: b.ConcreteResourceOrphan,
State: b.State,
Config: b.Config,
skip: b.Operation == walkPlanDestroy,
},
// We also need nodes for any deposed instance objects present in the
// state, so we can plan to destroy them. (During plan this will
// intentionally skip creating nodes for _current_ objects, since
// ConfigTransformer created nodes that will do that during
// DynamicExpand.)
&StateTransformer{
ConcreteCurrent: b.ConcreteResourceInstance,
ConcreteDeposed: b.ConcreteResourceInstanceDeposed,
State: b.State,
},
// Attach the state
&AttachStateTransformer{State: b.State},
// Create orphan output nodes
&OrphanOutputTransformer{
Config: b.Config,
State: b.State,
Planning: true,
},
// Attach the configuration to any resources
&AttachResourceConfigTransformer{Config: b.Config},
// add providers
transformProviders(b.ConcreteProvider, b.Config),
// Remove modules no longer present in the config
&RemovedModuleTransformer{Config: b.Config, State: b.State},
// Must attach schemas before ReferenceTransformer so that we can
// analyze the configuration to find references.
&AttachSchemaTransformer{Plugins: b.Plugins, Config: b.Config},
// Create expansion nodes for all of the module calls. This must
// come after all other transformers that create nodes representing
// objects that can belong to modules.
&ModuleExpansionTransformer{Concrete: b.ConcreteModule, Config: b.Config},
&ReferenceTransformer{},
&AttachDependenciesTransformer{},
// Make sure data sources are aware of any depends_on from the
// configuration
&attachDataResourceDependsOnTransformer{},
// DestroyEdgeTransformer is only required during a plan so that the
// TargetsTransformer can determine which nodes to keep in the graph.
&DestroyEdgeTransformer{},
&pruneUnusedNodesTransformer{
skip: b.Operation != walkPlanDestroy,
},
// Target
&TargetsTransformer{Targets: b.Targets},
// Detect when create_before_destroy must be forced on for a particular
// node due to dependency edges, to avoid graph cycles during apply.
&ForcedCBDTransformer{},
// Close opened plugin connections
&CloseProviderTransformer{},
// Close the root module
&CloseRootModuleTransformer{},
// Perform the transitive reduction to make our graph a bit
// more understandable if possible (it usually is possible).
&TransitiveReductionTransformer{},
}
return steps
}
func (b *PlanGraphBuilder) initPlan() {
b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
return &NodeApplyableProvider{
NodeAbstractProvider: a,
}
}
b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
return &nodeExpandPlannableResource{
NodeAbstractResource: a,
skipRefresh: b.skipRefresh,
skipPlanChanges: b.skipPlanChanges,
preDestroyRefresh: b.preDestroyRefresh,
forceReplace: b.ForceReplace,
}
}
b.ConcreteResourceOrphan = func(a *NodeAbstractResourceInstance) dag.Vertex {
return &NodePlannableResourceInstanceOrphan{
NodeAbstractResourceInstance: a,
skipRefresh: b.skipRefresh,
skipPlanChanges: b.skipPlanChanges,
}
}
b.ConcreteResourceInstanceDeposed = func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex {
return &NodePlanDeposedResourceInstanceObject{
NodeAbstractResourceInstance: a,
DeposedKey: key,
skipRefresh: b.skipRefresh,
skipPlanChanges: b.skipPlanChanges,
}
}
}
func (b *PlanGraphBuilder) initDestroy() {
b.initPlan()
b.ConcreteResourceInstance = func(a *NodeAbstractResourceInstance) dag.Vertex {
return &NodePlanDestroyableResourceInstance{
NodeAbstractResourceInstance: a,
skipRefresh: b.skipRefresh,
}
}
}
func (b *PlanGraphBuilder) initValidate() {
// Set the provider to the normal provider. This will ask for input.
b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
return &NodeApplyableProvider{
NodeAbstractProvider: a,
}
}
b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
return &NodeValidatableResource{
NodeAbstractResource: a,
}
}
b.ConcreteModule = func(n *nodeExpandModule) dag.Vertex {
return &nodeValidateModule{
nodeExpandModule: *n,
}
}
}
func (b *PlanGraphBuilder) initImport() {
b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex {
return &NodeApplyableProvider{
NodeAbstractProvider: a,
}
}
b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex {
return &nodeExpandPlannableResource{
NodeAbstractResource: a,
// For now we always skip planning changes for import, since we are
// not going to combine importing with other changes. This is
// temporary to try and maintain existing import behaviors, but
// planning will need to be allowed for more complex configurations.
skipPlanChanges: true,
// We also skip refresh for now, since the plan output is written
// as the new state, and users are not expecting the import process
// to update any other instances in state.
skipRefresh: true,
}
}
}