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