1package terraform 2 3import ( 4 "sync" 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 implements GraphBuilder and is responsible for building 14// a graph for planning (creating a Terraform Diff). 15// 16// The primary difference between this graph and others: 17// 18// * Based on the config since it represents the target state 19// 20// * Ignores lifecycle options since no lifecycle events occur here. This 21// simplifies the graph significantly since complex transforms such as 22// create-before-destroy can be completely ignored. 23// 24type PlanGraphBuilder struct { 25 // Config is the configuration tree to build a plan from. 26 Config *configs.Config 27 28 // State is the current state 29 State *states.State 30 31 // Components is a factory for the plug-in components (providers and 32 // provisioners) available for use. 33 Components contextComponentFactory 34 35 // Schemas is the repository of schemas we will draw from to analyse 36 // the configuration. 37 Schemas *Schemas 38 39 // Targets are resources to target 40 Targets []addrs.Targetable 41 42 // ForceReplace are resource instances where if we would normally have 43 // generated a NoOp or Update action then we'll force generating a replace 44 // action instead. Create and Delete actions are not affected. 45 ForceReplace []addrs.AbsResourceInstance 46 47 // Validate will do structural validation of the graph. 48 Validate bool 49 50 // skipRefresh indicates that we should skip refreshing managed resources 51 skipRefresh bool 52 53 // skipPlanChanges indicates that we should skip the step of comparing 54 // prior state with configuration and generating planned changes to 55 // resource instances. (This is for the "refresh only" planning mode, 56 // where we _only_ do the refresh step.) 57 skipPlanChanges bool 58 59 // CustomConcrete can be set to customize the node types created 60 // for various parts of the plan. This is useful in order to customize 61 // the plan behavior. 62 CustomConcrete bool 63 ConcreteProvider ConcreteProviderNodeFunc 64 ConcreteResource ConcreteResourceNodeFunc 65 ConcreteResourceOrphan ConcreteResourceInstanceNodeFunc 66 ConcreteModule ConcreteModuleNodeFunc 67 68 once sync.Once 69} 70 71// See GraphBuilder 72func (b *PlanGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { 73 return (&BasicGraphBuilder{ 74 Steps: b.Steps(), 75 Validate: b.Validate, 76 Name: "PlanGraphBuilder", 77 }).Build(path) 78} 79 80// See GraphBuilder 81func (b *PlanGraphBuilder) Steps() []GraphTransformer { 82 b.once.Do(b.init) 83 84 concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex { 85 return &NodePlanDeposedResourceInstanceObject{ 86 NodeAbstractResourceInstance: a, 87 DeposedKey: key, 88 89 skipRefresh: b.skipRefresh, 90 skipPlanChanges: b.skipPlanChanges, 91 } 92 } 93 94 steps := []GraphTransformer{ 95 // Creates all the resources represented in the config 96 &ConfigTransformer{ 97 Concrete: b.ConcreteResource, 98 Config: b.Config, 99 }, 100 101 // Add dynamic values 102 &RootVariableTransformer{Config: b.Config}, 103 &ModuleVariableTransformer{Config: b.Config}, 104 &LocalTransformer{Config: b.Config}, 105 &OutputTransformer{Config: b.Config}, 106 107 // Add orphan resources 108 &OrphanResourceInstanceTransformer{ 109 Concrete: b.ConcreteResourceOrphan, 110 State: b.State, 111 Config: b.Config, 112 }, 113 114 // We also need nodes for any deposed instance objects present in the 115 // state, so we can plan to destroy them. (This intentionally 116 // skips creating nodes for _current_ objects, since ConfigTransformer 117 // created nodes that will do that during DynamicExpand.) 118 &StateTransformer{ 119 ConcreteDeposed: concreteResourceInstanceDeposed, 120 State: b.State, 121 }, 122 123 // Attach the state 124 &AttachStateTransformer{State: b.State}, 125 126 // Create orphan output nodes 127 &OrphanOutputTransformer{Config: b.Config, State: b.State}, 128 129 // Attach the configuration to any resources 130 &AttachResourceConfigTransformer{Config: b.Config}, 131 132 // add providers 133 TransformProviders(b.Components.ResourceProviders(), b.ConcreteProvider, b.Config), 134 135 // Remove modules no longer present in the config 136 &RemovedModuleTransformer{Config: b.Config, State: b.State}, 137 138 // Must attach schemas before ReferenceTransformer so that we can 139 // analyze the configuration to find references. 140 &AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config}, 141 142 // Create expansion nodes for all of the module calls. This must 143 // come after all other transformers that create nodes representing 144 // objects that can belong to modules. 145 &ModuleExpansionTransformer{Concrete: b.ConcreteModule, Config: b.Config}, 146 147 // Connect so that the references are ready for targeting. We'll 148 // have to connect again later for providers and so on. 149 &ReferenceTransformer{}, 150 &AttachDependenciesTransformer{}, 151 152 // Make sure data sources are aware of any depends_on from the 153 // configuration 154 &attachDataResourceDependsOnTransformer{}, 155 156 // Target 157 &TargetsTransformer{Targets: b.Targets}, 158 159 // Detect when create_before_destroy must be forced on for a particular 160 // node due to dependency edges, to avoid graph cycles during apply. 161 &ForcedCBDTransformer{}, 162 163 // Add the node to fix the state count boundaries 164 &CountBoundaryTransformer{ 165 Config: b.Config, 166 }, 167 168 // Close opened plugin connections 169 &CloseProviderTransformer{}, 170 171 // Close the root module 172 &CloseRootModuleTransformer{}, 173 174 // Perform the transitive reduction to make our graph a bit 175 // more understandable if possible (it usually is possible). 176 &TransitiveReductionTransformer{}, 177 } 178 179 return steps 180} 181 182func (b *PlanGraphBuilder) init() { 183 // Do nothing if the user requests customizing the fields 184 if b.CustomConcrete { 185 return 186 } 187 188 b.ConcreteProvider = func(a *NodeAbstractProvider) dag.Vertex { 189 return &NodeApplyableProvider{ 190 NodeAbstractProvider: a, 191 } 192 } 193 194 b.ConcreteResource = func(a *NodeAbstractResource) dag.Vertex { 195 return &nodeExpandPlannableResource{ 196 NodeAbstractResource: a, 197 skipRefresh: b.skipRefresh, 198 skipPlanChanges: b.skipPlanChanges, 199 forceReplace: b.ForceReplace, 200 } 201 } 202 203 b.ConcreteResourceOrphan = func(a *NodeAbstractResourceInstance) dag.Vertex { 204 return &NodePlannableResourceInstanceOrphan{ 205 NodeAbstractResourceInstance: a, 206 skipRefresh: b.skipRefresh, 207 skipPlanChanges: b.skipPlanChanges, 208 } 209 } 210} 211