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