1package terraform 2 3import ( 4 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 5 "github.com/hashicorp/terraform-plugin-sdk/internal/configs" 6 "github.com/hashicorp/terraform-plugin-sdk/internal/dag" 7 "github.com/hashicorp/terraform-plugin-sdk/internal/plans" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 9 "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" 10) 11 12// ApplyGraphBuilder implements GraphBuilder and is responsible for building 13// a graph for applying a Terraform diff. 14// 15// Because the graph is built from the diff (vs. the config or state), 16// this helps ensure that the apply-time graph doesn't modify any resources 17// that aren't explicitly in the diff. There are other scenarios where the 18// diff can be deviated, so this is just one layer of protection. 19type ApplyGraphBuilder struct { 20 // Config is the configuration tree that the diff was built from. 21 Config *configs.Config 22 23 // Changes describes the changes that we need apply. 24 Changes *plans.Changes 25 26 // State is the current state 27 State *states.State 28 29 // Components is a factory for the plug-in components (providers and 30 // provisioners) available for use. 31 Components contextComponentFactory 32 33 // Schemas is the repository of schemas we will draw from to analyse 34 // the configuration. 35 Schemas *Schemas 36 37 // Targets are resources to target. This is only required to make sure 38 // unnecessary outputs aren't included in the apply graph. The plan 39 // builder successfully handles targeting resources. In the future, 40 // outputs should go into the diff so that this is unnecessary. 41 Targets []addrs.Targetable 42 43 // DisableReduce, if true, will not reduce the graph. Great for testing. 44 DisableReduce bool 45 46 // Destroy, if true, represents a pure destroy operation 47 Destroy bool 48 49 // Validate will do structural validation of the graph. 50 Validate bool 51} 52 53// See GraphBuilder 54func (b *ApplyGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { 55 return (&BasicGraphBuilder{ 56 Steps: b.Steps(), 57 Validate: b.Validate, 58 Name: "ApplyGraphBuilder", 59 }).Build(path) 60} 61 62// See GraphBuilder 63func (b *ApplyGraphBuilder) Steps() []GraphTransformer { 64 // Custom factory for creating providers. 65 concreteProvider := func(a *NodeAbstractProvider) dag.Vertex { 66 return &NodeApplyableProvider{ 67 NodeAbstractProvider: a, 68 } 69 } 70 71 concreteResource := func(a *NodeAbstractResource) dag.Vertex { 72 return &NodeApplyableResource{ 73 NodeAbstractResource: a, 74 } 75 } 76 77 concreteOrphanResource := func(a *NodeAbstractResource) dag.Vertex { 78 return &NodeDestroyResource{ 79 NodeAbstractResource: a, 80 } 81 } 82 83 concreteResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex { 84 return &NodeApplyableResourceInstance{ 85 NodeAbstractResourceInstance: a, 86 } 87 } 88 89 steps := []GraphTransformer{ 90 // Creates all the resources represented in the config. During apply, 91 // we use this just to ensure that the whole-resource metadata is 92 // updated to reflect things such as whether the count argument is 93 // set in config, or which provider configuration manages each resource. 94 &ConfigTransformer{ 95 Concrete: concreteResource, 96 Config: b.Config, 97 }, 98 99 // Creates all the resource instances represented in the diff, along 100 // with dependency edges against the whole-resource nodes added by 101 // ConfigTransformer above. 102 &DiffTransformer{ 103 Concrete: concreteResourceInstance, 104 State: b.State, 105 Changes: b.Changes, 106 }, 107 108 // Creates extra cleanup nodes for any entire resources that are 109 // no longer present in config, so we can make sure we clean up the 110 // leftover empty resource states after the instances have been 111 // destroyed. 112 // (We don't track this particular type of change in the plan because 113 // it's just cleanup of our own state object, and so doesn't effect 114 // any real remote objects or consumable outputs.) 115 &OrphanResourceTransformer{ 116 Concrete: concreteOrphanResource, 117 Config: b.Config, 118 State: b.State, 119 }, 120 121 // Create orphan output nodes 122 &OrphanOutputTransformer{Config: b.Config, State: b.State}, 123 124 // Attach the configuration to any resources 125 &AttachResourceConfigTransformer{Config: b.Config}, 126 127 // Attach the state 128 &AttachStateTransformer{State: b.State}, 129 130 // Destruction ordering 131 &DestroyEdgeTransformer{ 132 Config: b.Config, 133 State: b.State, 134 Schemas: b.Schemas, 135 }, 136 GraphTransformIf( 137 func() bool { return !b.Destroy }, 138 &CBDEdgeTransformer{ 139 Config: b.Config, 140 State: b.State, 141 Schemas: b.Schemas, 142 }, 143 ), 144 145 // Provisioner-related transformations 146 &MissingProvisionerTransformer{Provisioners: b.Components.ResourceProvisioners()}, 147 &ProvisionerTransformer{}, 148 149 // Add root variables 150 &RootVariableTransformer{Config: b.Config}, 151 152 // Add the local values 153 &LocalTransformer{Config: b.Config}, 154 155 // Add the outputs 156 &OutputTransformer{Config: b.Config}, 157 158 // Add module variables 159 &ModuleVariableTransformer{Config: b.Config}, 160 161 // add providers 162 TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config), 163 164 // Remove modules no longer present in the config 165 &RemovedModuleTransformer{Config: b.Config, State: b.State}, 166 167 // Must attach schemas before ReferenceTransformer so that we can 168 // analyze the configuration to find references. 169 &AttachSchemaTransformer{Schemas: b.Schemas}, 170 171 // Connect references so ordering is correct 172 &ReferenceTransformer{}, 173 174 // Handle destroy time transformations for output and local values. 175 // Reverse the edges from outputs and locals, so that 176 // interpolations don't fail during destroy. 177 // Create a destroy node for outputs to remove them from the state. 178 // Prune unreferenced values, which may have interpolations that can't 179 // be resolved. 180 GraphTransformIf( 181 func() bool { return b.Destroy }, 182 GraphTransformMulti( 183 &DestroyValueReferenceTransformer{}, 184 &DestroyOutputTransformer{}, 185 &PruneUnusedValuesTransformer{}, 186 ), 187 ), 188 189 // Add the node to fix the state count boundaries 190 &CountBoundaryTransformer{ 191 Config: b.Config, 192 }, 193 194 // Target 195 &TargetsTransformer{Targets: b.Targets}, 196 197 // Close opened plugin connections 198 &CloseProviderTransformer{}, 199 &CloseProvisionerTransformer{}, 200 201 // Single root 202 &RootTransformer{}, 203 } 204 205 if !b.DisableReduce { 206 // Perform the transitive reduction to make our graph a bit 207 // more sane if possible (it usually is possible). 208 steps = append(steps, &TransitiveReductionTransformer{}) 209 } 210 211 return steps 212} 213