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