diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/terraform/eval_state.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/terraform/eval_state.go | 324 |
1 files changed, 324 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_state.go b/vendor/github.com/hashicorp/terraform/terraform/eval_state.go new file mode 100644 index 00000000..126a0e63 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_state.go @@ -0,0 +1,324 @@ +package terraform + +import "fmt" + +// EvalReadState is an EvalNode implementation that reads the +// primary InstanceState for a specific resource out of the state. +type EvalReadState struct { + Name string + Output **InstanceState +} + +func (n *EvalReadState) Eval(ctx EvalContext) (interface{}, error) { + return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) { + return rs.Primary, nil + }) +} + +// EvalReadStateDeposed is an EvalNode implementation that reads the +// deposed InstanceState for a specific resource out of the state +type EvalReadStateDeposed struct { + Name string + Output **InstanceState + // Index indicates which instance in the Deposed list to target, or -1 for + // the last item. + Index int +} + +func (n *EvalReadStateDeposed) Eval(ctx EvalContext) (interface{}, error) { + return readInstanceFromState(ctx, n.Name, n.Output, func(rs *ResourceState) (*InstanceState, error) { + // Get the index. If it is negative, then we get the last one + idx := n.Index + if idx < 0 { + idx = len(rs.Deposed) - 1 + } + if idx >= 0 && idx < len(rs.Deposed) { + return rs.Deposed[idx], nil + } else { + return nil, fmt.Errorf("bad deposed index: %d, for resource: %#v", idx, rs) + } + }) +} + +// Does the bulk of the work for the various flavors of ReadState eval nodes. +// Each node just provides a reader function to get from the ResourceState to the +// InstanceState, and this takes care of all the plumbing. +func readInstanceFromState( + ctx EvalContext, + resourceName string, + output **InstanceState, + readerFn func(*ResourceState) (*InstanceState, error), +) (*InstanceState, error) { + state, lock := ctx.State() + + // Get a read lock so we can access this instance + lock.RLock() + defer lock.RUnlock() + + // Look for the module state. If we don't have one, then it doesn't matter. + mod := state.ModuleByPath(ctx.Path()) + if mod == nil { + return nil, nil + } + + // Look for the resource state. If we don't have one, then it is okay. + rs := mod.Resources[resourceName] + if rs == nil { + return nil, nil + } + + // Use the delegate function to get the instance state from the resource state + is, err := readerFn(rs) + if err != nil { + return nil, err + } + + // Write the result to the output pointer + if output != nil { + *output = is + } + + return is, nil +} + +// EvalRequireState is an EvalNode implementation that early exits +// if the state doesn't have an ID. +type EvalRequireState struct { + State **InstanceState +} + +func (n *EvalRequireState) Eval(ctx EvalContext) (interface{}, error) { + if n.State == nil { + return nil, EvalEarlyExitError{} + } + + state := *n.State + if state == nil || state.ID == "" { + return nil, EvalEarlyExitError{} + } + + return nil, nil +} + +// EvalUpdateStateHook is an EvalNode implementation that calls the +// PostStateUpdate hook with the current state. +type EvalUpdateStateHook struct{} + +func (n *EvalUpdateStateHook) Eval(ctx EvalContext) (interface{}, error) { + state, lock := ctx.State() + + // Get a full lock. Even calling something like WriteState can modify + // (prune) the state, so we need the full lock. + lock.Lock() + defer lock.Unlock() + + // Call the hook + err := ctx.Hook(func(h Hook) (HookAction, error) { + return h.PostStateUpdate(state) + }) + if err != nil { + return nil, err + } + + return nil, nil +} + +// EvalWriteState is an EvalNode implementation that writes the +// primary InstanceState for a specific resource into the state. +type EvalWriteState struct { + Name string + ResourceType string + Provider string + Dependencies []string + State **InstanceState +} + +func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) { + return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies, + func(rs *ResourceState) error { + rs.Primary = *n.State + return nil + }, + ) +} + +// EvalWriteStateDeposed is an EvalNode implementation that writes +// an InstanceState out to the Deposed list of a resource in the state. +type EvalWriteStateDeposed struct { + Name string + ResourceType string + Provider string + Dependencies []string + State **InstanceState + // Index indicates which instance in the Deposed list to target, or -1 to append. + Index int +} + +func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, error) { + return writeInstanceToState(ctx, n.Name, n.ResourceType, n.Provider, n.Dependencies, + func(rs *ResourceState) error { + if n.Index == -1 { + rs.Deposed = append(rs.Deposed, *n.State) + } else { + rs.Deposed[n.Index] = *n.State + } + return nil + }, + ) +} + +// Pulls together the common tasks of the EvalWriteState nodes. All the args +// are passed directly down from the EvalNode along with a `writer` function +// which is yielded the *ResourceState and is responsible for writing an +// InstanceState to the proper field in the ResourceState. +func writeInstanceToState( + ctx EvalContext, + resourceName string, + resourceType string, + provider string, + dependencies []string, + writerFn func(*ResourceState) error, +) (*InstanceState, error) { + state, lock := ctx.State() + if state == nil { + return nil, fmt.Errorf("cannot write state to nil state") + } + + // Get a write lock so we can access this instance + lock.Lock() + defer lock.Unlock() + + // Look for the module state. If we don't have one, create it. + mod := state.ModuleByPath(ctx.Path()) + if mod == nil { + mod = state.AddModule(ctx.Path()) + } + + // Look for the resource state. + rs := mod.Resources[resourceName] + if rs == nil { + rs = &ResourceState{} + rs.init() + mod.Resources[resourceName] = rs + } + rs.Type = resourceType + rs.Dependencies = dependencies + rs.Provider = provider + + if err := writerFn(rs); err != nil { + return nil, err + } + + return nil, nil +} + +// EvalClearPrimaryState is an EvalNode implementation that clears the primary +// instance from a resource state. +type EvalClearPrimaryState struct { + Name string +} + +func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) { + state, lock := ctx.State() + + // Get a read lock so we can access this instance + lock.RLock() + defer lock.RUnlock() + + // Look for the module state. If we don't have one, then it doesn't matter. + mod := state.ModuleByPath(ctx.Path()) + if mod == nil { + return nil, nil + } + + // Look for the resource state. If we don't have one, then it is okay. + rs := mod.Resources[n.Name] + if rs == nil { + return nil, nil + } + + // Clear primary from the resource state + rs.Primary = nil + + return nil, nil +} + +// EvalDeposeState is an EvalNode implementation that takes the primary +// out of a state and makes it Deposed. This is done at the beginning of +// create-before-destroy calls so that the create can create while preserving +// the old state of the to-be-destroyed resource. +type EvalDeposeState struct { + Name string +} + +// TODO: test +func (n *EvalDeposeState) Eval(ctx EvalContext) (interface{}, error) { + state, lock := ctx.State() + + // Get a read lock so we can access this instance + lock.RLock() + defer lock.RUnlock() + + // Look for the module state. If we don't have one, then it doesn't matter. + mod := state.ModuleByPath(ctx.Path()) + if mod == nil { + return nil, nil + } + + // Look for the resource state. If we don't have one, then it is okay. + rs := mod.Resources[n.Name] + if rs == nil { + return nil, nil + } + + // If we don't have a primary, we have nothing to depose + if rs.Primary == nil { + return nil, nil + } + + // Depose + rs.Deposed = append(rs.Deposed, rs.Primary) + rs.Primary = nil + + return nil, nil +} + +// EvalUndeposeState is an EvalNode implementation that reads the +// InstanceState for a specific resource out of the state. +type EvalUndeposeState struct { + Name string + State **InstanceState +} + +// TODO: test +func (n *EvalUndeposeState) Eval(ctx EvalContext) (interface{}, error) { + state, lock := ctx.State() + + // Get a read lock so we can access this instance + lock.RLock() + defer lock.RUnlock() + + // Look for the module state. If we don't have one, then it doesn't matter. + mod := state.ModuleByPath(ctx.Path()) + if mod == nil { + return nil, nil + } + + // Look for the resource state. If we don't have one, then it is okay. + rs := mod.Resources[n.Name] + if rs == nil { + return nil, nil + } + + // If we don't have any desposed resource, then we don't have anything to do + if len(rs.Deposed) == 0 { + return nil, nil + } + + // Undepose + idx := len(rs.Deposed) - 1 + rs.Primary = rs.Deposed[idx] + rs.Deposed[idx] = *n.State + + return nil, nil +} |