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