github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/node_resource_plan.go (about) 1 package terraform 2 3 import ( 4 "log" 5 6 "github.com/hashicorp/terraform-plugin-sdk/internal/dag" 7 "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" 8 ) 9 10 // NodePlannableResource represents a resource that is "plannable": 11 // it is ready to be planned in order to create a diff. 12 type NodePlannableResource struct { 13 *NodeAbstractResource 14 15 // ForceCreateBeforeDestroy might be set via our GraphNodeDestroyerCBD 16 // during graph construction, if dependencies require us to force this 17 // on regardless of what the configuration says. 18 ForceCreateBeforeDestroy *bool 19 } 20 21 var ( 22 _ GraphNodeSubPath = (*NodePlannableResource)(nil) 23 _ GraphNodeDestroyerCBD = (*NodePlannableResource)(nil) 24 _ GraphNodeDynamicExpandable = (*NodePlannableResource)(nil) 25 _ GraphNodeReferenceable = (*NodePlannableResource)(nil) 26 _ GraphNodeReferencer = (*NodePlannableResource)(nil) 27 _ GraphNodeResource = (*NodePlannableResource)(nil) 28 _ GraphNodeAttachResourceConfig = (*NodePlannableResource)(nil) 29 ) 30 31 // GraphNodeEvalable 32 func (n *NodePlannableResource) EvalTree() EvalNode { 33 addr := n.ResourceAddr() 34 config := n.Config 35 36 if config == nil { 37 // Nothing to do, then. 38 log.Printf("[TRACE] NodeApplyableResource: no configuration present for %s", addr) 39 return &EvalNoop{} 40 } 41 42 // this ensures we can reference the resource even if the count is 0 43 return &EvalWriteResourceState{ 44 Addr: addr.Resource, 45 Config: config, 46 ProviderAddr: n.ResolvedProvider, 47 } 48 } 49 50 // GraphNodeDestroyerCBD 51 func (n *NodePlannableResource) CreateBeforeDestroy() bool { 52 if n.ForceCreateBeforeDestroy != nil { 53 return *n.ForceCreateBeforeDestroy 54 } 55 56 // If we have no config, we just assume no 57 if n.Config == nil || n.Config.Managed == nil { 58 return false 59 } 60 61 return n.Config.Managed.CreateBeforeDestroy 62 } 63 64 // GraphNodeDestroyerCBD 65 func (n *NodePlannableResource) ModifyCreateBeforeDestroy(v bool) error { 66 n.ForceCreateBeforeDestroy = &v 67 return nil 68 } 69 70 // GraphNodeDynamicExpandable 71 func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { 72 var diags tfdiags.Diagnostics 73 74 count, countDiags := evaluateResourceCountExpression(n.Config.Count, ctx) 75 diags = diags.Append(countDiags) 76 if countDiags.HasErrors() { 77 return nil, diags.Err() 78 } 79 80 forEachMap, forEachDiags := evaluateResourceForEachExpression(n.Config.ForEach, ctx) 81 if forEachDiags.HasErrors() { 82 return nil, diags.Err() 83 } 84 85 // Next we need to potentially rename an instance address in the state 86 // if we're transitioning whether "count" is set at all. 87 fixResourceCountSetTransition(ctx, n.ResourceAddr(), count != -1) 88 89 // Our graph transformers require access to the full state, so we'll 90 // temporarily lock it while we work on this. 91 state := ctx.State().Lock() 92 defer ctx.State().Unlock() 93 94 // The concrete resource factory we'll use 95 concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex { 96 // Add the config and state since we don't do that via transforms 97 a.Config = n.Config 98 a.ResolvedProvider = n.ResolvedProvider 99 a.Schema = n.Schema 100 a.ProvisionerSchemas = n.ProvisionerSchemas 101 102 return &NodePlannableResourceInstance{ 103 NodeAbstractResourceInstance: a, 104 105 // By the time we're walking, we've figured out whether we need 106 // to force on CreateBeforeDestroy due to dependencies on other 107 // nodes that have it. 108 ForceCreateBeforeDestroy: n.CreateBeforeDestroy(), 109 } 110 } 111 112 // The concrete resource factory we'll use for orphans 113 concreteResourceOrphan := func(a *NodeAbstractResourceInstance) dag.Vertex { 114 // Add the config and state since we don't do that via transforms 115 a.Config = n.Config 116 a.ResolvedProvider = n.ResolvedProvider 117 a.Schema = n.Schema 118 a.ProvisionerSchemas = n.ProvisionerSchemas 119 120 return &NodePlannableResourceInstanceOrphan{ 121 NodeAbstractResourceInstance: a, 122 } 123 } 124 125 // Start creating the steps 126 steps := []GraphTransformer{ 127 // Expand the count or for_each (if present) 128 &ResourceCountTransformer{ 129 Concrete: concreteResource, 130 Schema: n.Schema, 131 Count: count, 132 ForEach: forEachMap, 133 Addr: n.ResourceAddr(), 134 }, 135 136 // Add the count/for_each orphans 137 &OrphanResourceCountTransformer{ 138 Concrete: concreteResourceOrphan, 139 Count: count, 140 ForEach: forEachMap, 141 Addr: n.ResourceAddr(), 142 State: state, 143 }, 144 145 // Attach the state 146 &AttachStateTransformer{State: state}, 147 148 // Targeting 149 &TargetsTransformer{Targets: n.Targets}, 150 151 // Connect references so ordering is correct 152 &ReferenceTransformer{}, 153 154 // Make sure there is a single root 155 &RootTransformer{}, 156 } 157 158 // Build the graph 159 b := &BasicGraphBuilder{ 160 Steps: steps, 161 Validate: true, 162 Name: "NodePlannableResource", 163 } 164 graph, diags := b.Build(ctx.Path()) 165 return graph, diags.ErrWithWarnings() 166 }