terraform: Refresh, Read/Write state
This commit is contained in:
parent
2b8fd18fa8
commit
1e962b868d
|
@ -83,7 +83,7 @@ func (c *Context2) Refresh() (*State, []error) {
|
|||
return nil, multierror.Append(errs, err).Errors
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return c.state, nil
|
||||
}
|
||||
|
||||
// Validate validates the configuration and returns any warnings or errors.
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/config"
|
||||
)
|
||||
|
||||
|
@ -42,6 +44,10 @@ type EvalContext interface {
|
|||
// The resource argument is optional. If given, it is the resource
|
||||
// that is currently being acted upon.
|
||||
Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error)
|
||||
|
||||
// State returns the global state as well as the lock that should
|
||||
// be used to modify that state.
|
||||
State() (*State, *sync.RWMutex)
|
||||
}
|
||||
|
||||
// MockEvalContext is a mock version of EvalContext that can be used
|
||||
|
@ -82,6 +88,10 @@ type MockEvalContext struct {
|
|||
|
||||
PathCalled bool
|
||||
PathPath []string
|
||||
|
||||
StateCalled bool
|
||||
StateState *State
|
||||
StateLock *sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) {
|
||||
|
@ -133,3 +143,8 @@ func (c *MockEvalContext) Path() []string {
|
|||
c.PathCalled = true
|
||||
return c.PathPath
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) State() (*State, *sync.RWMutex) {
|
||||
c.StateCalled = true
|
||||
return c.StateState, c.StateLock
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ type BuiltinEvalContext struct {
|
|||
Provisioners map[string]ResourceProvisionerFactory
|
||||
ProvisionerCache map[string]ResourceProvisioner
|
||||
ProvisionerLock *sync.Mutex
|
||||
StateValue *State
|
||||
StateLock *sync.RWMutex
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
@ -155,6 +157,10 @@ func (ctx *BuiltinEvalContext) Path() []string {
|
|||
return ctx.PathValue
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
|
||||
return ctx.StateValue, ctx.StateLock
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) init() {
|
||||
// We nil-check the things below because they're meant to be configured,
|
||||
// and we just default them to non-nil.
|
||||
|
|
|
@ -21,3 +21,38 @@ func EvalNodeFilterOp(op walkOperation) EvalNodeFilterFunc {
|
|||
return EvalNoop{}
|
||||
}
|
||||
}
|
||||
|
||||
// EvalOpFilter is an EvalNode implementation that is a proxy to
|
||||
// another node but filters based on the operation.
|
||||
type EvalOpFilter struct {
|
||||
// Ops is the list of operations to include this node in.
|
||||
Ops []walkOperation
|
||||
|
||||
// Node is the node to execute
|
||||
Node EvalNode
|
||||
}
|
||||
|
||||
func (n *EvalOpFilter) Args() ([]EvalNode, []EvalType) {
|
||||
return []EvalNode{n.Node}, []EvalType{n.Node.Type()}
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalOpFilter) Eval(
|
||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||
return args[0], nil
|
||||
}
|
||||
|
||||
func (n *EvalOpFilter) Type() EvalType {
|
||||
return n.Node.Type()
|
||||
}
|
||||
|
||||
// EvalNodeOpFilterable impl.
|
||||
func (n *EvalOpFilter) IncludeInOp(op walkOperation) bool {
|
||||
for _, v := range n.Ops {
|
||||
if v == op {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package terraform
|
||||
|
||||
// EvalRefresh is an EvalNode implementation that does a refresh for
|
||||
// a resource.
|
||||
type EvalRefresh struct {
|
||||
Provider EvalNode
|
||||
State EvalNode
|
||||
Info *InstanceInfo
|
||||
}
|
||||
|
||||
func (n *EvalRefresh) Args() ([]EvalNode, []EvalType) {
|
||||
return []EvalNode{n.Provider, n.State},
|
||||
[]EvalType{EvalTypeResourceProvider, EvalTypeInstanceState}
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalRefresh) Eval(
|
||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||
var state *InstanceState
|
||||
provider := args[0].(ResourceProvider)
|
||||
if args[1] != nil {
|
||||
state = args[1].(*InstanceState)
|
||||
}
|
||||
|
||||
n.Info.ModulePath = ctx.Path()
|
||||
return provider.Refresh(n.Info, state)
|
||||
}
|
||||
|
||||
func (n *EvalRefresh) Type() EvalType {
|
||||
return EvalTypeInstanceState
|
||||
}
|
|
@ -31,3 +31,10 @@ func (n *EvalSequence) Type() EvalType {
|
|||
|
||||
return n.Nodes[len(n.Nodes)-1].Type()
|
||||
}
|
||||
|
||||
// EvalNodeFilterable impl.
|
||||
func (n *EvalSequence) Filter(fn EvalNodeFilterFunc) {
|
||||
for i, node := range n.Nodes {
|
||||
n.Nodes[i] = fn(node)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEvalSequence_impl(t *testing.T) {
|
||||
var _ EvalNodeFilterable = new(EvalSequence)
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// EvalReadState is an EvalNode implementation that reads the
|
||||
// InstanceState for a specific resource out of the state.
|
||||
type EvalReadState struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (n *EvalReadState) Args() ([]EvalNode, []EvalType) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalReadState) Eval(
|
||||
ctx EvalContext, args []interface{}) (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
|
||||
}
|
||||
|
||||
// Return the primary
|
||||
return rs.Primary, nil
|
||||
}
|
||||
|
||||
func (n *EvalReadState) Type() EvalType {
|
||||
return EvalTypeInstanceState
|
||||
}
|
||||
|
||||
// EvalWriteState is an EvalNode implementation that reads the
|
||||
// InstanceState for a specific resource out of the state.
|
||||
type EvalWriteState struct {
|
||||
Name string
|
||||
ResourceType string
|
||||
Dependencies []string
|
||||
State EvalNode
|
||||
}
|
||||
|
||||
func (n *EvalWriteState) Args() ([]EvalNode, []EvalType) {
|
||||
return []EvalNode{n.State}, []EvalType{EvalTypeInstanceState}
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalWriteState) Eval(
|
||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||
var instanceState *InstanceState
|
||||
if args[0] != nil {
|
||||
instanceState = args[0].(*InstanceState)
|
||||
}
|
||||
|
||||
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[n.Name]
|
||||
if rs == nil {
|
||||
rs = &ResourceState{}
|
||||
rs.init()
|
||||
mod.Resources[n.Name] = rs
|
||||
}
|
||||
rs.Type = n.ResourceType
|
||||
rs.Dependencies = n.Dependencies
|
||||
|
||||
// Set the primary state
|
||||
rs.Primary = instanceState
|
||||
|
||||
// Prune because why not, we can clear out old useless entries now
|
||||
rs.prune()
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *EvalWriteState) Type() EvalType {
|
||||
return EvalTypeNull
|
||||
}
|
|
@ -17,4 +17,5 @@ const (
|
|||
EvalTypeConfig // *ResourceConfig
|
||||
EvalTypeResourceProvider // ResourceProvider
|
||||
EvalTypeResourceProvisioner // ResourceProvisioner
|
||||
EvalTypeInstanceState // *InstanceState
|
||||
)
|
||||
|
|
|
@ -10,6 +10,7 @@ const (
|
|||
_EvalType_name_2 = "EvalTypeConfig"
|
||||
_EvalType_name_3 = "EvalTypeResourceProvider"
|
||||
_EvalType_name_4 = "EvalTypeResourceProvisioner"
|
||||
_EvalType_name_5 = "EvalTypeInstanceState"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -18,6 +19,7 @@ var (
|
|||
_EvalType_index_2 = [...]uint8{0, 14}
|
||||
_EvalType_index_3 = [...]uint8{0, 24}
|
||||
_EvalType_index_4 = [...]uint8{0, 27}
|
||||
_EvalType_index_5 = [...]uint8{0, 21}
|
||||
)
|
||||
|
||||
func (i EvalType) String() string {
|
||||
|
@ -32,6 +34,8 @@ func (i EvalType) String() string {
|
|||
return _EvalType_name_3
|
||||
case i == 16:
|
||||
return _EvalType_name_4
|
||||
case i == 32:
|
||||
return _EvalType_name_5
|
||||
default:
|
||||
return fmt.Sprintf("EvalType(%d)", i)
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
|||
Provisioners: w.Context.provisioners,
|
||||
ProvisionerCache: w.provisionerCache,
|
||||
ProvisionerLock: &w.provisionerLock,
|
||||
StateValue: w.Context.state,
|
||||
StateLock: &w.Context.stateLock,
|
||||
Interpolater: &Interpolater{
|
||||
Operation: w.Operation,
|
||||
Module: w.Context.module,
|
||||
|
|
|
@ -96,10 +96,29 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
|||
})
|
||||
}
|
||||
|
||||
// Refresh the resource
|
||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkRefresh},
|
||||
Node: &EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &EvalRefresh{
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()},
|
||||
State: &EvalReadState{Name: n.stateId()},
|
||||
Info: &InstanceInfo{Id: n.stateId(), Type: n.Resource.Type},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return seq
|
||||
}
|
||||
|
||||
// stateId is the name used for the state key
|
||||
func (n *graphNodeExpandedResource) stateId() string {
|
||||
if n.Index == 0 {
|
||||
return n.Resource.Id()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s.%d", n.Resource.Id(), n.Index)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue