github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/node_resource_plan_instance.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform-plugin-sdk/internal/plans" 7 "github.com/hashicorp/terraform-plugin-sdk/internal/providers" 8 "github.com/hashicorp/terraform-plugin-sdk/internal/states" 9 10 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 11 "github.com/zclconf/go-cty/cty" 12 ) 13 14 // NodePlannableResourceInstance represents a _single_ resource 15 // instance that is plannable. This means this represents a single 16 // count index, for example. 17 type NodePlannableResourceInstance struct { 18 *NodeAbstractResourceInstance 19 ForceCreateBeforeDestroy bool 20 } 21 22 var ( 23 _ GraphNodeSubPath = (*NodePlannableResourceInstance)(nil) 24 _ GraphNodeReferenceable = (*NodePlannableResourceInstance)(nil) 25 _ GraphNodeReferencer = (*NodePlannableResourceInstance)(nil) 26 _ GraphNodeResource = (*NodePlannableResourceInstance)(nil) 27 _ GraphNodeResourceInstance = (*NodePlannableResourceInstance)(nil) 28 _ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstance)(nil) 29 _ GraphNodeAttachResourceState = (*NodePlannableResourceInstance)(nil) 30 _ GraphNodeEvalable = (*NodePlannableResourceInstance)(nil) 31 ) 32 33 // GraphNodeEvalable 34 func (n *NodePlannableResourceInstance) EvalTree() EvalNode { 35 addr := n.ResourceInstanceAddr() 36 37 // Eval info is different depending on what kind of resource this is 38 switch addr.Resource.Resource.Mode { 39 case addrs.ManagedResourceMode: 40 return n.evalTreeManagedResource(addr) 41 case addrs.DataResourceMode: 42 return n.evalTreeDataResource(addr) 43 default: 44 panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) 45 } 46 } 47 48 func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResourceInstance) EvalNode { 49 config := n.Config 50 var provider providers.Interface 51 var providerSchema *ProviderSchema 52 var change *plans.ResourceInstanceChange 53 var state *states.ResourceInstanceObject 54 var configVal cty.Value 55 56 return &EvalSequence{ 57 Nodes: []EvalNode{ 58 &EvalGetProvider{ 59 Addr: n.ResolvedProvider, 60 Output: &provider, 61 Schema: &providerSchema, 62 }, 63 64 &EvalReadState{ 65 Addr: addr.Resource, 66 Provider: &provider, 67 ProviderSchema: &providerSchema, 68 69 Output: &state, 70 }, 71 72 // If we already have a non-planned state then we already dealt 73 // with this during the refresh walk and so we have nothing to do 74 // here. 75 &EvalIf{ 76 If: func(ctx EvalContext) (bool, error) { 77 depChanges := false 78 79 // Check and see if any of our dependencies have changes. 80 changes := ctx.Changes() 81 for _, d := range n.StateReferences() { 82 ri, ok := d.(addrs.ResourceInstance) 83 if !ok { 84 continue 85 } 86 change := changes.GetResourceInstanceChange(ri.Absolute(ctx.Path()), states.CurrentGen) 87 if change != nil && change.Action != plans.NoOp { 88 depChanges = true 89 break 90 } 91 } 92 93 refreshed := state != nil && state.Status != states.ObjectPlanned 94 95 // If there are no dependency changes, and it's not a forced 96 // read because we there was no Refresh, then we don't need 97 // to re-read. If any dependencies have changes, it means 98 // our config may also have changes and we need to Read the 99 // data source again. 100 if !depChanges && refreshed { 101 return false, EvalEarlyExitError{} 102 } 103 return true, nil 104 }, 105 Then: EvalNoop{}, 106 }, 107 108 &EvalValidateSelfRef{ 109 Addr: addr.Resource, 110 Config: config.Config, 111 ProviderSchema: &providerSchema, 112 }, 113 114 &EvalReadData{ 115 Addr: addr.Resource, 116 Config: n.Config, 117 Dependencies: n.StateReferences(), 118 Provider: &provider, 119 ProviderAddr: n.ResolvedProvider, 120 ProviderSchema: &providerSchema, 121 ForcePlanRead: true, // _always_ produce a Read change, even if the config seems ready 122 OutputChange: &change, 123 OutputValue: &configVal, 124 OutputState: &state, 125 }, 126 127 &EvalWriteState{ 128 Addr: addr.Resource, 129 ProviderAddr: n.ResolvedProvider, 130 ProviderSchema: &providerSchema, 131 State: &state, 132 }, 133 134 &EvalWriteDiff{ 135 Addr: addr.Resource, 136 ProviderSchema: &providerSchema, 137 Change: &change, 138 }, 139 }, 140 } 141 } 142 143 func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsResourceInstance) EvalNode { 144 config := n.Config 145 var provider providers.Interface 146 var providerSchema *ProviderSchema 147 var change *plans.ResourceInstanceChange 148 var state *states.ResourceInstanceObject 149 150 return &EvalSequence{ 151 Nodes: []EvalNode{ 152 &EvalGetProvider{ 153 Addr: n.ResolvedProvider, 154 Output: &provider, 155 Schema: &providerSchema, 156 }, 157 158 &EvalReadState{ 159 Addr: addr.Resource, 160 Provider: &provider, 161 ProviderSchema: &providerSchema, 162 163 Output: &state, 164 }, 165 166 &EvalValidateSelfRef{ 167 Addr: addr.Resource, 168 Config: config.Config, 169 ProviderSchema: &providerSchema, 170 }, 171 172 &EvalDiff{ 173 Addr: addr.Resource, 174 Config: n.Config, 175 CreateBeforeDestroy: n.ForceCreateBeforeDestroy, 176 Provider: &provider, 177 ProviderAddr: n.ResolvedProvider, 178 ProviderSchema: &providerSchema, 179 State: &state, 180 OutputChange: &change, 181 OutputState: &state, 182 }, 183 &EvalCheckPreventDestroy{ 184 Addr: addr.Resource, 185 Config: n.Config, 186 Change: &change, 187 }, 188 &EvalWriteState{ 189 Addr: addr.Resource, 190 ProviderAddr: n.ResolvedProvider, 191 State: &state, 192 ProviderSchema: &providerSchema, 193 }, 194 &EvalWriteDiff{ 195 Addr: addr.Resource, 196 ProviderSchema: &providerSchema, 197 Change: &change, 198 }, 199 }, 200 } 201 }