mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-30 10:47:14 -06:00
21b0a03d70
Adds an "alias" field to the provider which allows creating multiple instances of a provider under different names. This provides support for configurations such as multiple AWS providers for different regions. In each resource, the provider can be set with the "provider" field. (thanks to Cisco Cloud for their support)
377 lines
9.7 KiB
Go
377 lines
9.7 KiB
Go
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
|
|
})
|
|
}
|
|
|
|
// EvalReadStateTainted is an EvalNode implementation that reads a
|
|
// tainted InstanceState for a specific resource out of the state
|
|
type EvalReadStateTainted struct {
|
|
Name string
|
|
Output **InstanceState
|
|
// Index indicates which instance in the Tainted list to target, or -1 for
|
|
// the last item.
|
|
Index int
|
|
}
|
|
|
|
func (n *EvalReadStateTainted) 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.Tainted) - 1
|
|
}
|
|
if idx >= 0 && idx < len(rs.Tainted) {
|
|
return rs.Tainted[idx], nil
|
|
} else {
|
|
return nil, fmt.Errorf("bad tainted index: %d, for resource: %#v", idx, rs)
|
|
}
|
|
})
|
|
}
|
|
|
|
// 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 read lock so it doesn't change while we're calling this
|
|
lock.RLock()
|
|
defer lock.RUnlock()
|
|
|
|
// 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
|
|
},
|
|
)
|
|
}
|
|
|
|
// EvalWriteStateTainted is an EvalNode implementation that writes
|
|
// an InstanceState out to the Tainted list of a resource in the state.
|
|
type EvalWriteStateTainted struct {
|
|
Name string
|
|
ResourceType string
|
|
Provider string
|
|
Dependencies []string
|
|
State **InstanceState
|
|
// Index indicates which instance in the Tainted list to target, or -1 to append.
|
|
Index int
|
|
}
|
|
|
|
// EvalWriteStateTainted is an EvalNode implementation that writes the
|
|
// one of the tainted InstanceStates for a specific resource out of the state.
|
|
func (n *EvalWriteStateTainted) 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.Tainted = append(rs.Tainted, *n.State)
|
|
} else {
|
|
rs.Tainted[n.Index] = *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
|
|
}
|
|
|
|
// 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] = nil
|
|
|
|
return nil, nil
|
|
}
|