github.com/rhenning/terraform@v0.8.0-beta2/terraform/node_resource_apply.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform/config" 7 ) 8 9 // NodeApplyableResource represents a resource that is "applyable": 10 // it is ready to be applied and is represented by a diff. 11 type NodeApplyableResource struct { 12 *NodeAbstractResource 13 } 14 15 // GraphNodeCreator 16 func (n *NodeApplyableResource) CreateAddr() *ResourceAddress { 17 return n.NodeAbstractResource.Addr 18 } 19 20 // GraphNodeEvalable 21 func (n *NodeApplyableResource) EvalTree() EvalNode { 22 addr := n.NodeAbstractResource.Addr 23 24 // stateId is the ID to put into the state 25 stateId := addr.stateId() 26 if addr.Index > -1 { 27 stateId = fmt.Sprintf("%s.%d", stateId, addr.Index) 28 } 29 30 // Build the instance info. More of this will be populated during eval 31 info := &InstanceInfo{ 32 Id: stateId, 33 Type: addr.Type, 34 } 35 36 // Build the resource for eval 37 resource := &Resource{ 38 Name: addr.Name, 39 Type: addr.Type, 40 CountIndex: addr.Index, 41 } 42 if resource.CountIndex < 0 { 43 resource.CountIndex = 0 44 } 45 46 // Determine the dependencies for the state. We use some older 47 // code for this that we've used for a long time. 48 var stateDeps []string 49 { 50 oldN := &graphNodeExpandedResource{Resource: n.Config} 51 stateDeps = oldN.StateDependencies() 52 } 53 54 // Eval info is different depending on what kind of resource this is 55 switch n.Config.Mode { 56 case config.ManagedResourceMode: 57 return n.evalTreeManagedResource( 58 stateId, info, resource, stateDeps, 59 ) 60 case config.DataResourceMode: 61 return n.evalTreeDataResource( 62 stateId, info, resource, stateDeps) 63 default: 64 panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) 65 } 66 } 67 68 func (n *NodeApplyableResource) evalTreeDataResource( 69 stateId string, info *InstanceInfo, 70 resource *Resource, stateDeps []string) EvalNode { 71 var provider ResourceProvider 72 var config *ResourceConfig 73 var diff *InstanceDiff 74 var state *InstanceState 75 76 return &EvalSequence{ 77 Nodes: []EvalNode{ 78 // Build the instance info 79 &EvalInstanceInfo{ 80 Info: info, 81 }, 82 83 // Get the saved diff for apply 84 &EvalReadDiff{ 85 Name: stateId, 86 Diff: &diff, 87 }, 88 89 // Stop here if we don't actually have a diff 90 &EvalIf{ 91 If: func(ctx EvalContext) (bool, error) { 92 if diff == nil { 93 return true, EvalEarlyExitError{} 94 } 95 96 if diff.GetAttributesLen() == 0 { 97 return true, EvalEarlyExitError{} 98 } 99 100 return true, nil 101 }, 102 Then: EvalNoop{}, 103 }, 104 105 // We need to re-interpolate the config here, rather than 106 // just using the diff's values directly, because we've 107 // potentially learned more variable values during the 108 // apply pass that weren't known when the diff was produced. 109 &EvalInterpolate{ 110 Config: n.Config.RawConfig.Copy(), 111 Resource: resource, 112 Output: &config, 113 }, 114 115 &EvalGetProvider{ 116 Name: n.ProvidedBy()[0], 117 Output: &provider, 118 }, 119 120 // Make a new diff with our newly-interpolated config. 121 &EvalReadDataDiff{ 122 Info: info, 123 Config: &config, 124 Previous: &diff, 125 Provider: &provider, 126 Output: &diff, 127 }, 128 129 &EvalReadDataApply{ 130 Info: info, 131 Diff: &diff, 132 Provider: &provider, 133 Output: &state, 134 }, 135 136 &EvalWriteState{ 137 Name: stateId, 138 ResourceType: n.Config.Type, 139 Provider: n.Config.Provider, 140 Dependencies: stateDeps, 141 State: &state, 142 }, 143 144 // Clear the diff now that we've applied it, so 145 // later nodes won't see a diff that's now a no-op. 146 &EvalWriteDiff{ 147 Name: stateId, 148 Diff: nil, 149 }, 150 151 &EvalUpdateStateHook{}, 152 }, 153 } 154 } 155 156 func (n *NodeApplyableResource) evalTreeManagedResource( 157 stateId string, info *InstanceInfo, 158 resource *Resource, stateDeps []string) EvalNode { 159 // Declare a bunch of variables that are used for state during 160 // evaluation. Most of this are written to by-address below. 161 var provider ResourceProvider 162 var diff, diffApply *InstanceDiff 163 var state *InstanceState 164 var resourceConfig *ResourceConfig 165 var err error 166 var createNew bool 167 var createBeforeDestroyEnabled bool 168 169 return &EvalSequence{ 170 Nodes: []EvalNode{ 171 // Build the instance info 172 &EvalInstanceInfo{ 173 Info: info, 174 }, 175 176 // Get the saved diff for apply 177 &EvalReadDiff{ 178 Name: stateId, 179 Diff: &diffApply, 180 }, 181 182 // We don't want to do any destroys 183 &EvalIf{ 184 If: func(ctx EvalContext) (bool, error) { 185 if diffApply == nil { 186 return true, EvalEarlyExitError{} 187 } 188 189 if diffApply.GetDestroy() && diffApply.GetAttributesLen() == 0 { 190 return true, EvalEarlyExitError{} 191 } 192 193 diffApply.SetDestroy(false) 194 return true, nil 195 }, 196 Then: EvalNoop{}, 197 }, 198 199 &EvalIf{ 200 If: func(ctx EvalContext) (bool, error) { 201 destroy := false 202 if diffApply != nil { 203 destroy = diffApply.GetDestroy() || diffApply.RequiresNew() 204 } 205 206 createBeforeDestroyEnabled = 207 n.Config.Lifecycle.CreateBeforeDestroy && 208 destroy 209 210 return createBeforeDestroyEnabled, nil 211 }, 212 Then: &EvalDeposeState{ 213 Name: stateId, 214 }, 215 }, 216 217 &EvalInterpolate{ 218 Config: n.Config.RawConfig.Copy(), 219 Resource: resource, 220 Output: &resourceConfig, 221 }, 222 &EvalGetProvider{ 223 Name: n.ProvidedBy()[0], 224 Output: &provider, 225 }, 226 &EvalReadState{ 227 Name: stateId, 228 Output: &state, 229 }, 230 // Re-run validation to catch any errors we missed, e.g. type 231 // mismatches on computed values. 232 &EvalValidateResource{ 233 Provider: &provider, 234 Config: &resourceConfig, 235 ResourceName: n.Config.Name, 236 ResourceType: n.Config.Type, 237 ResourceMode: n.Config.Mode, 238 IgnoreWarnings: true, 239 }, 240 &EvalDiff{ 241 Info: info, 242 Config: &resourceConfig, 243 Resource: n.Config, 244 Provider: &provider, 245 Diff: &diffApply, 246 State: &state, 247 OutputDiff: &diffApply, 248 }, 249 250 // Get the saved diff 251 &EvalReadDiff{ 252 Name: stateId, 253 Diff: &diff, 254 }, 255 256 // Compare the diffs 257 &EvalCompareDiff{ 258 Info: info, 259 One: &diff, 260 Two: &diffApply, 261 }, 262 263 &EvalGetProvider{ 264 Name: n.ProvidedBy()[0], 265 Output: &provider, 266 }, 267 &EvalReadState{ 268 Name: stateId, 269 Output: &state, 270 }, 271 &EvalApply{ 272 Info: info, 273 State: &state, 274 Diff: &diffApply, 275 Provider: &provider, 276 Output: &state, 277 Error: &err, 278 CreateNew: &createNew, 279 }, 280 &EvalWriteState{ 281 Name: stateId, 282 ResourceType: n.Config.Type, 283 Provider: n.Config.Provider, 284 Dependencies: stateDeps, 285 State: &state, 286 }, 287 &EvalApplyProvisioners{ 288 Info: info, 289 State: &state, 290 Resource: n.Config, 291 InterpResource: resource, 292 CreateNew: &createNew, 293 Error: &err, 294 }, 295 &EvalIf{ 296 If: func(ctx EvalContext) (bool, error) { 297 return createBeforeDestroyEnabled && err != nil, nil 298 }, 299 Then: &EvalUndeposeState{ 300 Name: stateId, 301 State: &state, 302 }, 303 Else: &EvalWriteState{ 304 Name: stateId, 305 ResourceType: n.Config.Type, 306 Provider: n.Config.Provider, 307 Dependencies: stateDeps, 308 State: &state, 309 }, 310 }, 311 312 // We clear the diff out here so that future nodes 313 // don't see a diff that is already complete. There 314 // is no longer a diff! 315 &EvalWriteDiff{ 316 Name: stateId, 317 Diff: nil, 318 }, 319 320 &EvalApplyPost{ 321 Info: info, 322 State: &state, 323 Error: &err, 324 }, 325 &EvalUpdateStateHook{}, 326 }, 327 } 328 }