github.com/pdecat/terraform@v0.11.9-beta1/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 // EvalDeposeState is an EvalNode implementation that takes the primary 218 // out of a state and makes it Deposed. This is done at the beginning of 219 // create-before-destroy calls so that the create can create while preserving 220 // the old state of the to-be-destroyed resource. 221 type EvalDeposeState struct { 222 Name string 223 } 224 225 // TODO: test 226 func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) { 227 state, lock := ctx.State() 228 229 // Get a read lock so we can access this instance 230 lock.RLock() 231 defer lock.RUnlock() 232 233 // Look for the module state. If we don't have one, then it doesn't matter. 234 mod := state.ModuleByPath(ctx.Path()) 235 if mod == nil { 236 return nil, nil 237 } 238 239 // Look for the resource state. If we don't have one, then it is okay. 240 rs := mod.Resources[n.Name] 241 if rs == nil { 242 return nil, nil 243 } 244 245 // If we don't have a primary, we have nothing to depose 246 if rs.Primary == nil { 247 return nil, nil 248 } 249 250 // Depose 251 rs.Deposed = append(rs.Deposed, rs.Primary) 252 rs.Primary = nil 253 254 return nil, nil 255 } 256 257 // EvalUndeposeState is an EvalNode implementation that reads the 258 // InstanceState for a specific resource out of the state. 259 type EvalUndeposeState struct { 260 Name string 261 State **InstanceState 262 } 263 264 // TODO: test 265 func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) { 266 state, lock := ctx.State() 267 268 // Get a read lock so we can access this instance 269 lock.RLock() 270 defer lock.RUnlock() 271 272 // Look for the module state. If we don't have one, then it doesn't matter. 273 mod := state.ModuleByPath(ctx.Path()) 274 if mod == nil { 275 return nil, nil 276 } 277 278 // Look for the resource state. If we don't have one, then it is okay. 279 rs := mod.Resources[n.Name] 280 if rs == nil { 281 return nil, nil 282 } 283 284 // If we don't have any desposed resource, then we don't have anything to do 285 if len(rs.Deposed) == 0 { 286 return nil, nil 287 } 288 289 // Undepose 290 idx := len(rs.Deposed) - 1 291 rs.Primary = rs.Deposed[idx] 292 rs.Deposed[idx] = *n.State 293 294 return nil, nil 295 }