github.com/nicgrayson/terraform@v0.4.3-0.20150415203910-c4de50829380/terraform/eval_state.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 ) 6 7 // EvalReadState is an EvalNode implementation that reads the 8 // primary InstanceState for a specific resource out of the state. 9 type EvalReadState struct { 10 Name string 11 Output **InstanceState 12 } 13 14 func (n *EvalReadState) Eval(ctx EvalContext) (interface{}, error) { 15 return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) { 16 return rs.Primary, nil 17 }) 18 } 19 20 // EvalReadStateTainted is an EvalNode implementation that reads a 21 // tainted InstanceState for a specific resource out of the state 22 type EvalReadStateTainted struct { 23 Name string 24 Output **InstanceState 25 // Index indicates which instance in the Tainted list to target, or -1 for 26 // the last item. 27 Index int 28 } 29 30 func (n *EvalReadStateTainted) Eval(ctx EvalContext) (interface{}, error) { 31 return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) { 32 // Get the index. If it is negative, then we get the last one 33 idx := n.Index 34 if idx < 0 { 35 idx = len(rs.Tainted) - 1 36 } 37 if idx >= 0 && idx < len(rs.Tainted) { 38 return rs.Tainted[idx], nil 39 } else { 40 return nil, fmt.Errorf("bad tainted index: %d, for resource: %#v", idx, rs) 41 } 42 }) 43 } 44 45 // EvalReadStateDeposed is an EvalNode implementation that reads the 46 // deposed InstanceState for a specific resource out of the state 47 type EvalReadStateDeposed struct { 48 Name string 49 Output **InstanceState 50 // Index indicates which instance in the Deposed list to target, or -1 for 51 // the last item. 52 Index int 53 } 54 55 func (n *EvalReadStateDeposed) Eval(ctx EvalContext) (interface{}, error) { 56 return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) { 57 // Get the index. If it is negative, then we get the last one 58 idx := n.Index 59 if idx < 0 { 60 idx = len(rs.Deposed) - 1 61 } 62 if idx >= 0 && idx < len(rs.Deposed) { 63 return rs.Deposed[idx], nil 64 } else { 65 return nil, fmt.Errorf("bad deposed index: %d, for resource: %#v", idx, rs) 66 } 67 }) 68 } 69 70 // Does the bulk of the work for the various flavors of ReadState eval nodes. 71 // Each node just provides a reader function to get from the ResourceState to the 72 // InstanceState, and this takes care of all the plumbing. 73 func readInstanceFromState( 74 ctx EvalContext, 75 resourceName string, 76 output **InstanceState, 77 readerFn func(*ResourceState) (*InstanceState, error), 78 ) (*InstanceState, error) { 79 state, lock := ctx.State() 80 81 // Get a read lock so we can access this instance 82 lock.RLock() 83 defer lock.RUnlock() 84 85 // Look for the module state. If we don't have one, then it doesn't matter. 86 mod := state.ModuleByPath(ctx.Path()) 87 if mod == nil { 88 return nil, nil 89 } 90 91 // Look for the resource state. If we don't have one, then it is okay. 92 rs := mod.Resources[resourceName] 93 if rs == nil { 94 return nil, nil 95 } 96 97 // Use the delegate function to get the instance state from the resource state 98 is, err := readerFn(rs) 99 if err != nil { 100 return nil, err 101 } 102 103 // Write the result to the output pointer 104 if output != nil { 105 *output = is 106 } 107 108 return is, nil 109 } 110 111 // EvalRequireState is an EvalNode implementation that early exits 112 // if the state doesn't have an ID. 113 type EvalRequireState struct { 114 State **InstanceState 115 } 116 117 func (n *EvalRequireState) Eval(ctx EvalContext) (interface{}, error) { 118 if n.State == nil { 119 return nil, EvalEarlyExitError{} 120 } 121 122 state := *n.State 123 if state == nil || state.ID == "" { 124 return nil, EvalEarlyExitError{} 125 } 126 127 return nil, nil 128 } 129 130 // EvalUpdateStateHook is an EvalNode implementation that calls the 131 // PostStateUpdate hook with the current state. 132 type EvalUpdateStateHook struct{} 133 134 func (n *EvalUpdateStateHook) Eval(ctx EvalContext) (interface{}, error) { 135 state, lock := ctx.State() 136 137 // Get a read lock so it doesn't change while we're calling this 138 lock.RLock() 139 defer lock.RUnlock() 140 141 // Call the hook 142 err := ctx.Hook(func(h Hook) (HookAction, error) { 143 return h.PostStateUpdate(state) 144 }) 145 if err != nil { 146 return nil, err 147 } 148 149 return nil, nil 150 } 151 152 // EvalWriteState is an EvalNode implementation that writes the 153 // primary InstanceState for a specific resource into the state. 154 type EvalWriteState struct { 155 Name string 156 ResourceType string 157 Dependencies []string 158 State **InstanceState 159 } 160 161 func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { 162 return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Dependencies, 163 func(rs *ResourceState) error { 164 rs.Primary = *n.State 165 return nil 166 }, 167 ) 168 } 169 170 // EvalWriteStateTainted is an EvalNode implementation that writes 171 // an InstanceState out to the Tainted list of a resource in the state. 172 type EvalWriteStateTainted struct { 173 Name string 174 ResourceType string 175 Dependencies []string 176 State **InstanceState 177 // Index indicates which instance in the Tainted list to target, or -1 to append. 178 Index int 179 } 180 181 // EvalWriteStateTainted is an EvalNode implementation that writes the 182 // one of the tainted InstanceStates for a specific resource out of the state. 183 func (n *EvalWriteStateTainted) Eval(ctx EvalContext) (interface{}, error) { 184 return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Dependencies, 185 func(rs *ResourceState) error { 186 if n.Index == -1 { 187 rs.Tainted = append(rs.Tainted, *n.State) 188 } else { 189 rs.Tainted[n.Index] = *n.State 190 } 191 return nil 192 }, 193 ) 194 } 195 196 // EvalWriteStateDeposed is an EvalNode implementation that writes 197 // an InstanceState out to the Deposed list of a resource in the state. 198 type EvalWriteStateDeposed struct { 199 Name string 200 ResourceType string 201 Dependencies []string 202 State **InstanceState 203 // Index indicates which instance in the Deposed list to target, or -1 to append. 204 Index int 205 } 206 207 func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) { 208 return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Dependencies, 209 func(rs *ResourceState) error { 210 if n.Index == -1 { 211 rs.Deposed = append(rs.Deposed, *n.State) 212 } else { 213 rs.Deposed[n.Index] = *n.State 214 } 215 return nil 216 }, 217 ) 218 } 219 220 // Pulls together the common tasks of the EvalWriteState nodes. All the args 221 // are passed directly down from the EvalNode along with a `writer` function 222 // which is yielded the *ResourceState and is responsible for writing an 223 // InstanceState to the proper field in the ResourceState. 224 func writeInstanceToState( 225 ctx EvalContext, 226 resourceName string, 227 resourceType string, 228 dependencies []string, 229 writerFn func(*ResourceState) error, 230 ) (*InstanceState, error) { 231 state, lock := ctx.State() 232 if state == nil { 233 return nil, fmt.Errorf("cannot write state to nil state") 234 } 235 236 // Get a write lock so we can access this instance 237 lock.Lock() 238 defer lock.Unlock() 239 240 // Look for the module state. If we don't have one, create it. 241 mod := state.ModuleByPath(ctx.Path()) 242 if mod == nil { 243 mod = state.AddModule(ctx.Path()) 244 } 245 246 // Look for the resource state. 247 rs := mod.Resources[resourceName] 248 if rs == nil { 249 rs = &ResourceState{} 250 rs.init() 251 mod.Resources[resourceName] = rs 252 } 253 rs.Type = resourceType 254 rs.Dependencies = dependencies 255 256 if err := writerFn(rs); err != nil { 257 return nil, err 258 } 259 260 return nil, nil 261 } 262 263 // EvalClearPrimaryState is an EvalNode implementation that clears the primary 264 // instance from a resource state. 265 type EvalClearPrimaryState struct { 266 Name string 267 } 268 269 func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) { 270 state, lock := ctx.State() 271 272 // Get a read lock so we can access this instance 273 lock.RLock() 274 defer lock.RUnlock() 275 276 // Look for the module state. If we don't have one, then it doesn't matter. 277 mod := state.ModuleByPath(ctx.Path()) 278 if mod == nil { 279 return nil, nil 280 } 281 282 // Look for the resource state. If we don't have one, then it is okay. 283 rs := mod.Resources[n.Name] 284 if rs == nil { 285 return nil, nil 286 } 287 288 // Clear primary from the resource state 289 rs.Primary = nil 290 291 return nil, nil 292 } 293 294 // EvalDeposeState is an EvalNode implementation that takes the primary 295 // out of a state and makes it Deposed. This is done at the beginning of 296 // create-before-destroy calls so that the create can create while preserving 297 // the old state of the to-be-destroyed resource. 298 type EvalDeposeState struct { 299 Name string 300 } 301 302 // TODO: test 303 func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) { 304 state, lock := ctx.State() 305 306 // Get a read lock so we can access this instance 307 lock.RLock() 308 defer lock.RUnlock() 309 310 // Look for the module state. If we don't have one, then it doesn't matter. 311 mod := state.ModuleByPath(ctx.Path()) 312 if mod == nil { 313 return nil, nil 314 } 315 316 // Look for the resource state. If we don't have one, then it is okay. 317 rs := mod.Resources[n.Name] 318 if rs == nil { 319 return nil, nil 320 } 321 322 // If we don't have a primary, we have nothing to depose 323 if rs.Primary == nil { 324 return nil, nil 325 } 326 327 // Depose 328 rs.Deposed = append(rs.Deposed, rs.Primary) 329 rs.Primary = nil 330 331 return nil, nil 332 } 333 334 // EvalUndeposeState is an EvalNode implementation that reads the 335 // InstanceState for a specific resource out of the state. 336 type EvalUndeposeState struct { 337 Name string 338 } 339 340 // TODO: test 341 func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) { 342 state, lock := ctx.State() 343 344 // Get a read lock so we can access this instance 345 lock.RLock() 346 defer lock.RUnlock() 347 348 // Look for the module state. If we don't have one, then it doesn't matter. 349 mod := state.ModuleByPath(ctx.Path()) 350 if mod == nil { 351 return nil, nil 352 } 353 354 // Look for the resource state. If we don't have one, then it is okay. 355 rs := mod.Resources[n.Name] 356 if rs == nil { 357 return nil, nil 358 } 359 360 // If we don't have any desposed resource, then we don't have anything to do 361 if len(rs.Deposed) == 0 { 362 return nil, nil 363 } 364 365 // Undepose 366 idx := len(rs.Deposed) - 1 367 rs.Primary = rs.Deposed[idx] 368 rs.Deposed[idx] = nil 369 370 return nil, nil 371 }