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, multierror.Append(errs, err).Errors
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return c.state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates the configuration and returns any warnings or errors.
|
// Validate validates the configuration and returns any warnings or errors.
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package terraform
|
package terraform
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/config"
|
"github.com/hashicorp/terraform/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,6 +44,10 @@ type EvalContext interface {
|
||||||
// The resource argument is optional. If given, it is the resource
|
// The resource argument is optional. If given, it is the resource
|
||||||
// that is currently being acted upon.
|
// that is currently being acted upon.
|
||||||
Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error)
|
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
|
// MockEvalContext is a mock version of EvalContext that can be used
|
||||||
|
@ -82,6 +88,10 @@ type MockEvalContext struct {
|
||||||
|
|
||||||
PathCalled bool
|
PathCalled bool
|
||||||
PathPath []string
|
PathPath []string
|
||||||
|
|
||||||
|
StateCalled bool
|
||||||
|
StateState *State
|
||||||
|
StateLock *sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) {
|
func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) {
|
||||||
|
@ -133,3 +143,8 @@ func (c *MockEvalContext) Path() []string {
|
||||||
c.PathCalled = true
|
c.PathCalled = true
|
||||||
return c.PathPath
|
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
|
Provisioners map[string]ResourceProvisionerFactory
|
||||||
ProvisionerCache map[string]ResourceProvisioner
|
ProvisionerCache map[string]ResourceProvisioner
|
||||||
ProvisionerLock *sync.Mutex
|
ProvisionerLock *sync.Mutex
|
||||||
|
StateValue *State
|
||||||
|
StateLock *sync.RWMutex
|
||||||
|
|
||||||
once sync.Once
|
once sync.Once
|
||||||
}
|
}
|
||||||
|
@ -155,6 +157,10 @@ func (ctx *BuiltinEvalContext) Path() []string {
|
||||||
return ctx.PathValue
|
return ctx.PathValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
|
||||||
|
return ctx.StateValue, ctx.StateLock
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *BuiltinEvalContext) init() {
|
func (ctx *BuiltinEvalContext) init() {
|
||||||
// We nil-check the things below because they're meant to be configured,
|
// We nil-check the things below because they're meant to be configured,
|
||||||
// and we just default them to non-nil.
|
// and we just default them to non-nil.
|
||||||
|
|
|
@ -21,3 +21,38 @@ func EvalNodeFilterOp(op walkOperation) EvalNodeFilterFunc {
|
||||||
return EvalNoop{}
|
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()
|
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
|
EvalTypeConfig // *ResourceConfig
|
||||||
EvalTypeResourceProvider // ResourceProvider
|
EvalTypeResourceProvider // ResourceProvider
|
||||||
EvalTypeResourceProvisioner // ResourceProvisioner
|
EvalTypeResourceProvisioner // ResourceProvisioner
|
||||||
|
EvalTypeInstanceState // *InstanceState
|
||||||
)
|
)
|
||||||
|
|
|
@ -10,6 +10,7 @@ const (
|
||||||
_EvalType_name_2 = "EvalTypeConfig"
|
_EvalType_name_2 = "EvalTypeConfig"
|
||||||
_EvalType_name_3 = "EvalTypeResourceProvider"
|
_EvalType_name_3 = "EvalTypeResourceProvider"
|
||||||
_EvalType_name_4 = "EvalTypeResourceProvisioner"
|
_EvalType_name_4 = "EvalTypeResourceProvisioner"
|
||||||
|
_EvalType_name_5 = "EvalTypeInstanceState"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -18,6 +19,7 @@ var (
|
||||||
_EvalType_index_2 = [...]uint8{0, 14}
|
_EvalType_index_2 = [...]uint8{0, 14}
|
||||||
_EvalType_index_3 = [...]uint8{0, 24}
|
_EvalType_index_3 = [...]uint8{0, 24}
|
||||||
_EvalType_index_4 = [...]uint8{0, 27}
|
_EvalType_index_4 = [...]uint8{0, 27}
|
||||||
|
_EvalType_index_5 = [...]uint8{0, 21}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i EvalType) String() string {
|
func (i EvalType) String() string {
|
||||||
|
@ -32,6 +34,8 @@ func (i EvalType) String() string {
|
||||||
return _EvalType_name_3
|
return _EvalType_name_3
|
||||||
case i == 16:
|
case i == 16:
|
||||||
return _EvalType_name_4
|
return _EvalType_name_4
|
||||||
|
case i == 32:
|
||||||
|
return _EvalType_name_5
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("EvalType(%d)", i)
|
return fmt.Sprintf("EvalType(%d)", i)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,8 @@ func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
||||||
Provisioners: w.Context.provisioners,
|
Provisioners: w.Context.provisioners,
|
||||||
ProvisionerCache: w.provisionerCache,
|
ProvisionerCache: w.provisionerCache,
|
||||||
ProvisionerLock: &w.provisionerLock,
|
ProvisionerLock: &w.provisionerLock,
|
||||||
|
StateValue: w.Context.state,
|
||||||
|
StateLock: &w.Context.stateLock,
|
||||||
Interpolater: &Interpolater{
|
Interpolater: &Interpolater{
|
||||||
Operation: w.Operation,
|
Operation: w.Operation,
|
||||||
Module: w.Context.module,
|
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
|
return seq
|
||||||
}
|
}
|
||||||
|
|
||||||
// stateId is the name used for the state key
|
// stateId is the name used for the state key
|
||||||
func (n *graphNodeExpandedResource) stateId() string {
|
func (n *graphNodeExpandedResource) stateId() string {
|
||||||
|
if n.Index == 0 {
|
||||||
|
return n.Resource.Id()
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%s.%d", n.Resource.Id(), n.Index)
|
return fmt.Sprintf("%s.%d", n.Resource.Id(), n.Index)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue