github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/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 full lock. Even calling something like WriteState can modify 111 // (prune) the state, so we need the full lock. 112 lock.Lock() 113 defer lock.Unlock() 114 115 // Call the hook 116 err := ctx.Hook(func(h Hook) (HookAction, error) { 117 return h.PostStateUpdate(state) 118 }) 119 if err != nil { 120 return nil, err 121 } 122 123 return nil, nil 124 } 125 126 // EvalWriteState is an EvalNode implementation that writes the 127 // primary InstanceState for a specific resource into the state. 128 type EvalWriteState struct { 129 Name string 130 ResourceType string 131 Provider string 132 Dependencies []string 133 State **InstanceState 134 } 135 136 func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { 137 return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies, 138 func(rs *ResourceState) error { 139 rs.Primary = *n.State 140 return nil 141 }, 142 ) 143 } 144 145 // EvalWriteStateDeposed is an EvalNode implementation that writes 146 // an InstanceState out to the Deposed list of a resource in the state. 147 type EvalWriteStateDeposed struct { 148 Name string 149 ResourceType string 150 Provider string 151 Dependencies []string 152 State **InstanceState 153 // Index indicates which instance in the Deposed list to target, or -1 to append. 154 Index int 155 } 156 157 func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) { 158 return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies, 159 func(rs *ResourceState) error { 160 if n.Index == -1 { 161 rs.Deposed = append(rs.Deposed, *n.State) 162 } else { 163 rs.Deposed[n.Index] = *n.State 164 } 165 return nil 166 }, 167 ) 168 } 169 170 // Pulls together the common tasks of the EvalWriteState nodes. All the args 171 // are passed directly down from the EvalNode along with a `writer` function 172 // which is yielded the *ResourceState and is responsible for writing an 173 // InstanceState to the proper field in the ResourceState. 174 func writeInstanceToState( 175 ctx EvalContext, 176 resourceName string, 177 resourceType string, 178 provider string, 179 dependencies []string, 180 writerFn func(*ResourceState) error, 181 ) (*InstanceState, error) { 182 state, lock := ctx.State() 183 if state == nil { 184 return nil, fmt.Errorf("cannot write state to nil state") 185 } 186 187 // Get a write lock so we can access this instance 188 lock.Lock() 189 defer lock.Unlock() 190 191 // Look for the module state. If we don't have one, create it. 192 mod := state.ModuleByPath(ctx.Path()) 193 if mod == nil { 194 mod = state.AddModule(ctx.Path()) 195 } 196 197 // Look for the resource state. 198 rs := mod.Resources[resourceName] 199 if rs == nil { 200 rs = &ResourceState{} 201 rs.init() 202 mod.Resources[resourceName] = rs 203 } 204 rs.Type = resourceType 205 rs.Dependencies = dependencies 206 rs.Provider = provider 207 208 if err := writerFn(rs); err != nil { 209 return nil, err 210 } 211 212 return nil, nil 213 } 214 215 // EvalClearPrimaryState is an EvalNode implementation that clears the primary 216 // instance from a resource state. 217 type EvalClearPrimaryState struct { 218 Name string 219 } 220 221 func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) { 222 state, lock := ctx.State() 223 224 // Get a read lock so we can access this instance 225 lock.RLock() 226 defer lock.RUnlock() 227 228 // Look for the module state. If we don't have one, then it doesn't matter. 229 mod := state.ModuleByPath(ctx.Path()) 230 if mod == nil { 231 return nil, nil 232 } 233 234 // Look for the resource state. If we don't have one, then it is okay. 235 rs := mod.Resources[n.Name] 236 if rs == nil { 237 return nil, nil 238 } 239 240 // Clear primary from the resource state 241 rs.Primary = nil 242 243 return nil, nil 244 } 245 246 // EvalDeposeState is an EvalNode implementation that takes the primary 247 // out of a state and makes it Deposed. This is done at the beginning of 248 // create-before-destroy calls so that the create can create while preserving 249 // the old state of the to-be-destroyed resource. 250 type EvalDeposeState struct { 251 Name string 252 } 253 254 // TODO: test 255 func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) { 256 state, lock := ctx.State() 257 258 // Get a read lock so we can access this instance 259 lock.RLock() 260 defer lock.RUnlock() 261 262 // Look for the module state. If we don't have one, then it doesn't matter. 263 mod := state.ModuleByPath(ctx.Path()) 264 if mod == nil { 265 return nil, nil 266 } 267 268 // Look for the resource state. If we don't have one, then it is okay. 269 rs := mod.Resources[n.Name] 270 if rs == nil { 271 return nil, nil 272 } 273 274 // If we don't have a primary, we have nothing to depose 275 if rs.Primary == nil { 276 return nil, nil 277 } 278 279 // Depose 280 rs.Deposed = append(rs.Deposed, rs.Primary) 281 rs.Primary = nil 282 283 return nil, nil 284 } 285 286 // EvalUndeposeState is an EvalNode implementation that reads the 287 // InstanceState for a specific resource out of the state. 288 type EvalUndeposeState struct { 289 Name string 290 State **InstanceState 291 } 292 293 // TODO: test 294 func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) { 295 state, lock := ctx.State() 296 297 // Get a read lock so we can access this instance 298 lock.RLock() 299 defer lock.RUnlock() 300 301 // Look for the module state. If we don't have one, then it doesn't matter. 302 mod := state.ModuleByPath(ctx.Path()) 303 if mod == nil { 304 return nil, nil 305 } 306 307 // Look for the resource state. If we don't have one, then it is okay. 308 rs := mod.Resources[n.Name] 309 if rs == nil { 310 return nil, nil 311 } 312 313 // If we don't have any desposed resource, then we don't have anything to do 314 if len(rs.Deposed) == 0 { 315 return nil, nil 316 } 317 318 // Undepose 319 idx := len(rs.Deposed) - 1 320 rs.Primary = rs.Deposed[idx] 321 rs.Deposed[idx] = *n.State 322 323 return nil, nil 324 }