github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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 Provider string 158 Dependencies []string 159 State **InstanceState 160 } 161 162 func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { 163 return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies, 164 func(rs *ResourceState) error { 165 rs.Primary = *n.State 166 return nil 167 }, 168 ) 169 } 170 171 // EvalWriteStateTainted is an EvalNode implementation that writes 172 // an InstanceState out to the Tainted list of a resource in the state. 173 type EvalWriteStateTainted struct { 174 Name string 175 ResourceType string 176 Provider string 177 Dependencies []string 178 State **InstanceState 179 // Index indicates which instance in the Tainted list to target, or -1 to append. 180 Index int 181 } 182 183 // EvalWriteStateTainted is an EvalNode implementation that writes the 184 // one of the tainted InstanceStates for a specific resource out of the state. 185 func (n *EvalWriteStateTainted) Eval(ctx EvalContext) (interface{}, error) { 186 return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies, 187 func(rs *ResourceState) error { 188 if n.Index == -1 { 189 rs.Tainted = append(rs.Tainted, *n.State) 190 } else { 191 rs.Tainted[n.Index] = *n.State 192 } 193 return nil 194 }, 195 ) 196 } 197 198 // EvalWriteStateDeposed is an EvalNode implementation that writes 199 // an InstanceState out to the Deposed list of a resource in the state. 200 type EvalWriteStateDeposed struct { 201 Name string 202 ResourceType string 203 Provider string 204 Dependencies []string 205 State **InstanceState 206 // Index indicates which instance in the Deposed list to target, or -1 to append. 207 Index int 208 } 209 210 func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) { 211 return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies, 212 func(rs *ResourceState) error { 213 if n.Index == -1 { 214 rs.Deposed = append(rs.Deposed, *n.State) 215 } else { 216 rs.Deposed[n.Index] = *n.State 217 } 218 return nil 219 }, 220 ) 221 } 222 223 // Pulls together the common tasks of the EvalWriteState nodes. All the args 224 // are passed directly down from the EvalNode along with a `writer` function 225 // which is yielded the *ResourceState and is responsible for writing an 226 // InstanceState to the proper field in the ResourceState. 227 func writeInstanceToState( 228 ctx EvalContext, 229 resourceName string, 230 resourceType string, 231 provider string, 232 dependencies []string, 233 writerFn func(*ResourceState) error, 234 ) (*InstanceState, error) { 235 state, lock := ctx.State() 236 if state == nil { 237 return nil, fmt.Errorf("cannot write state to nil state") 238 } 239 240 // Get a write lock so we can access this instance 241 lock.Lock() 242 defer lock.Unlock() 243 244 // Look for the module state. If we don't have one, create it. 245 mod := state.ModuleByPath(ctx.Path()) 246 if mod == nil { 247 mod = state.AddModule(ctx.Path()) 248 } 249 250 // Look for the resource state. 251 rs := mod.Resources[resourceName] 252 if rs == nil { 253 rs = &ResourceState{} 254 rs.init() 255 mod.Resources[resourceName] = rs 256 } 257 rs.Type = resourceType 258 rs.Dependencies = dependencies 259 rs.Provider = provider 260 261 if err := writerFn(rs); err != nil { 262 return nil, err 263 } 264 265 return nil, nil 266 } 267 268 // EvalClearPrimaryState is an EvalNode implementation that clears the primary 269 // instance from a resource state. 270 type EvalClearPrimaryState struct { 271 Name string 272 } 273 274 func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) { 275 state, lock := ctx.State() 276 277 // Get a read lock so we can access this instance 278 lock.RLock() 279 defer lock.RUnlock() 280 281 // Look for the module state. If we don't have one, then it doesn't matter. 282 mod := state.ModuleByPath(ctx.Path()) 283 if mod == nil { 284 return nil, nil 285 } 286 287 // Look for the resource state. If we don't have one, then it is okay. 288 rs := mod.Resources[n.Name] 289 if rs == nil { 290 return nil, nil 291 } 292 293 // Clear primary from the resource state 294 rs.Primary = nil 295 296 return nil, nil 297 } 298 299 // EvalDeposeState is an EvalNode implementation that takes the primary 300 // out of a state and makes it Deposed. This is done at the beginning of 301 // create-before-destroy calls so that the create can create while preserving 302 // the old state of the to-be-destroyed resource. 303 type EvalDeposeState struct { 304 Name string 305 } 306 307 // TODO: test 308 func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) { 309 state, lock := ctx.State() 310 311 // Get a read lock so we can access this instance 312 lock.RLock() 313 defer lock.RUnlock() 314 315 // Look for the module state. If we don't have one, then it doesn't matter. 316 mod := state.ModuleByPath(ctx.Path()) 317 if mod == nil { 318 return nil, nil 319 } 320 321 // Look for the resource state. If we don't have one, then it is okay. 322 rs := mod.Resources[n.Name] 323 if rs == nil { 324 return nil, nil 325 } 326 327 // If we don't have a primary, we have nothing to depose 328 if rs.Primary == nil { 329 return nil, nil 330 } 331 332 // Depose 333 rs.Deposed = append(rs.Deposed, rs.Primary) 334 rs.Primary = nil 335 336 return nil, nil 337 } 338 339 // EvalUndeposeState is an EvalNode implementation that reads the 340 // InstanceState for a specific resource out of the state. 341 type EvalUndeposeState struct { 342 Name string 343 } 344 345 // TODO: test 346 func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) { 347 state, lock := ctx.State() 348 349 // Get a read lock so we can access this instance 350 lock.RLock() 351 defer lock.RUnlock() 352 353 // Look for the module state. If we don't have one, then it doesn't matter. 354 mod := state.ModuleByPath(ctx.Path()) 355 if mod == nil { 356 return nil, nil 357 } 358 359 // Look for the resource state. If we don't have one, then it is okay. 360 rs := mod.Resources[n.Name] 361 if rs == nil { 362 return nil, nil 363 } 364 365 // If we don't have any desposed resource, then we don't have anything to do 366 if len(rs.Deposed) == 0 { 367 return nil, nil 368 } 369 370 // Undepose 371 idx := len(rs.Deposed) - 1 372 rs.Primary = rs.Deposed[idx] 373 rs.Deposed[idx] = nil 374 375 return nil, nil 376 }