terraform: starting up the plans
This commit is contained in:
parent
a78fe784b8
commit
aae2d4c780
|
@ -26,6 +26,7 @@ type ContextOpts struct {
|
|||
// perform operations on infrastructure. This structure is built using
|
||||
// NewContext. See the documentation for that.
|
||||
type Context2 struct {
|
||||
diff *Diff
|
||||
hooks []Hook
|
||||
module *module.Tree
|
||||
providers map[string]ResourceProviderFactory
|
||||
|
@ -41,12 +42,19 @@ type Context2 struct {
|
|||
// should not be mutated in any way, since the pointers are copied, not
|
||||
// the values themselves.
|
||||
func NewContext2(opts *ContextOpts) *Context2 {
|
||||
state := opts.State
|
||||
if state == nil {
|
||||
state = new(State)
|
||||
state.init()
|
||||
}
|
||||
|
||||
return &Context2{
|
||||
diff: opts.Diff,
|
||||
hooks: opts.Hooks,
|
||||
module: opts.Module,
|
||||
providers: opts.Providers,
|
||||
provisioners: opts.Provisioners,
|
||||
state: opts.State,
|
||||
state: state,
|
||||
variables: opts.Variables,
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +81,51 @@ func (c *Context2) GraphBuilder() GraphBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
// Plan generates an execution plan for the given context.
|
||||
//
|
||||
// The execution plan encapsulates the context and can be stored
|
||||
// in order to reinstantiate a context later for Apply.
|
||||
//
|
||||
// Plan also updates the diff of this context to be the diff generated
|
||||
// by the plan, so Apply can be called after.
|
||||
func (c *Context2) Plan(opts *PlanOpts) (*Plan, error) {
|
||||
p := &Plan{
|
||||
Module: c.module,
|
||||
Vars: c.variables,
|
||||
State: c.state,
|
||||
}
|
||||
|
||||
var operation walkOperation
|
||||
if opts != nil && opts.Destroy {
|
||||
operation = walkPlanDestroy
|
||||
} else {
|
||||
// Set our state to be something temporary. We do this so that
|
||||
// the plan can update a fake state so that variables work, then
|
||||
// we replace it back with our old state.
|
||||
old := c.state
|
||||
if old == nil {
|
||||
c.state = &State{}
|
||||
c.state.init()
|
||||
} else {
|
||||
c.state = old.deepcopy()
|
||||
}
|
||||
defer func() {
|
||||
c.state = old
|
||||
}()
|
||||
|
||||
operation = walkPlan
|
||||
}
|
||||
|
||||
// Do the walk
|
||||
walker, err := c.walk(operation)
|
||||
p.Diff = walker.Diff
|
||||
|
||||
// Update the diff so that our context is up-to-date
|
||||
c.diff = p.Diff
|
||||
|
||||
return p, err
|
||||
}
|
||||
|
||||
// Refresh goes through all the resources in the state and refreshes them
|
||||
// to their latest state. This will update the state that this context
|
||||
// works with, along with returning it.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -49,6 +49,10 @@ type EvalContext interface {
|
|||
// that is currently being acted upon.
|
||||
Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error)
|
||||
|
||||
// Diff returns the global diff as well as the lock that should
|
||||
// be used to modify that diff.
|
||||
Diff() (*Diff, *sync.RWMutex)
|
||||
|
||||
// State returns the global state as well as the lock that should
|
||||
// be used to modify that state.
|
||||
State() (*State, *sync.RWMutex)
|
||||
|
@ -96,6 +100,10 @@ type MockEvalContext struct {
|
|||
PathCalled bool
|
||||
PathPath []string
|
||||
|
||||
DiffCalled bool
|
||||
DiffDiff *Diff
|
||||
DiffLock *sync.RWMutex
|
||||
|
||||
StateCalled bool
|
||||
StateState *State
|
||||
StateLock *sync.RWMutex
|
||||
|
@ -156,6 +164,11 @@ func (c *MockEvalContext) Path() []string {
|
|||
return c.PathPath
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) Diff() (*Diff, *sync.RWMutex) {
|
||||
c.DiffCalled = true
|
||||
return c.DiffDiff, c.DiffLock
|
||||
}
|
||||
|
||||
func (c *MockEvalContext) State() (*State, *sync.RWMutex) {
|
||||
c.StateCalled = true
|
||||
return c.StateState, c.StateLock
|
||||
|
|
|
@ -22,6 +22,8 @@ type BuiltinEvalContext struct {
|
|||
Provisioners map[string]ResourceProvisionerFactory
|
||||
ProvisionerCache map[string]ResourceProvisioner
|
||||
ProvisionerLock *sync.Mutex
|
||||
DiffValue *Diff
|
||||
DiffLock *sync.RWMutex
|
||||
StateValue *State
|
||||
StateLock *sync.RWMutex
|
||||
|
||||
|
@ -177,6 +179,10 @@ func (ctx *BuiltinEvalContext) Path() []string {
|
|||
return ctx.PathValue
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) {
|
||||
return ctx.DiffValue, ctx.DiffLock
|
||||
}
|
||||
|
||||
func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
|
||||
return ctx.StateValue, ctx.StateLock
|
||||
}
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
package terraform
|
||||
|
||||
// EvalDiff is an EvalNode implementation that does a refresh for
|
||||
// a resource.
|
||||
type EvalDiff struct {
|
||||
Info *InstanceInfo
|
||||
Config EvalNode
|
||||
Provider EvalNode
|
||||
State EvalNode
|
||||
Output *InstanceDiff
|
||||
}
|
||||
|
||||
func (n *EvalDiff) Args() ([]EvalNode, []EvalType) {
|
||||
return []EvalNode{n.Config, n.Provider, n.State},
|
||||
[]EvalType{EvalTypeConfig, EvalTypeResourceProvider,
|
||||
EvalTypeInstanceState}
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalDiff) Eval(
|
||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||
// Extract our arguments
|
||||
var state *InstanceState
|
||||
config := args[0].(*ResourceConfig)
|
||||
provider := args[1].(ResourceProvider)
|
||||
if args[2] != nil {
|
||||
state = args[2].(*InstanceState)
|
||||
}
|
||||
|
||||
// Call pre-diff hook
|
||||
err := ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
return h.PreDiff(n.Info, state)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The state for the diff must never be nil
|
||||
diffState := state
|
||||
if diffState == nil {
|
||||
diffState = new(InstanceState)
|
||||
}
|
||||
diffState.init()
|
||||
|
||||
// Diff!
|
||||
diff, err := provider.Diff(n.Info, diffState, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if diff == nil {
|
||||
diff = new(InstanceDiff)
|
||||
}
|
||||
|
||||
// Require a destroy if there is no ID and it requires new.
|
||||
if diff.RequiresNew() && state != nil && state.ID != "" {
|
||||
diff.Destroy = true
|
||||
}
|
||||
|
||||
// If we're creating a new resource, compute its ID
|
||||
if diff.RequiresNew() || state == nil || state.ID == "" {
|
||||
var oldID string
|
||||
if state != nil {
|
||||
oldID = state.Attributes["id"]
|
||||
}
|
||||
|
||||
// Add diff to compute new ID
|
||||
diff.init()
|
||||
diff.Attributes["id"] = &ResourceAttrDiff{
|
||||
Old: oldID,
|
||||
NewComputed: true,
|
||||
RequiresNew: true,
|
||||
Type: DiffAttrOutput,
|
||||
}
|
||||
}
|
||||
|
||||
// Call post-refresh hook
|
||||
err = ctx.Hook(func(h Hook) (HookAction, error) {
|
||||
return h.PostDiff(n.Info, diff)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update our output
|
||||
*n.Output = *diff
|
||||
|
||||
// Merge our state so that the state is updated with our plan
|
||||
if !diff.Empty() {
|
||||
state = state.MergeDiff(diff)
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
func (n *EvalDiff) Type() EvalType {
|
||||
return EvalTypeInstanceState
|
||||
}
|
||||
|
||||
// EvalWriteDiff is an EvalNode implementation that writes the diff to
|
||||
// the full diff.
|
||||
type EvalWriteDiff struct {
|
||||
Name string
|
||||
Diff *InstanceDiff
|
||||
}
|
||||
|
||||
func (n *EvalWriteDiff) Args() ([]EvalNode, []EvalType) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalWriteDiff) Eval(
|
||||
ctx EvalContext, args []interface{}) (interface{}, error) {
|
||||
diff, lock := ctx.Diff()
|
||||
|
||||
// Acquire the lock so that we can do this safely concurrently
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
// Write the diff
|
||||
modDiff := diff.ModuleByPath(ctx.Path())
|
||||
if modDiff == nil {
|
||||
modDiff = diff.AddModule(ctx.Path())
|
||||
}
|
||||
modDiff.Resources[n.Name] = n.Diff
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *EvalWriteDiff) Type() EvalType {
|
||||
return EvalTypeNull
|
||||
}
|
|
@ -17,5 +17,6 @@ const (
|
|||
EvalTypeConfig // *ResourceConfig
|
||||
EvalTypeResourceProvider // ResourceProvider
|
||||
EvalTypeResourceProvisioner // ResourceProvisioner
|
||||
EvalTypeInstanceDiff // *InstanceDiff
|
||||
EvalTypeInstanceState // *InstanceState
|
||||
)
|
||||
|
|
|
@ -10,7 +10,8 @@ const (
|
|||
_EvalType_name_2 = "EvalTypeConfig"
|
||||
_EvalType_name_3 = "EvalTypeResourceProvider"
|
||||
_EvalType_name_4 = "EvalTypeResourceProvisioner"
|
||||
_EvalType_name_5 = "EvalTypeInstanceState"
|
||||
_EvalType_name_5 = "EvalTypeInstanceDiff"
|
||||
_EvalType_name_6 = "EvalTypeInstanceState"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -19,7 +20,8 @@ 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}
|
||||
_EvalType_index_5 = [...]uint8{0, 20}
|
||||
_EvalType_index_6 = [...]uint8{0, 21}
|
||||
)
|
||||
|
||||
func (i EvalType) String() string {
|
||||
|
@ -36,6 +38,8 @@ func (i EvalType) String() string {
|
|||
return _EvalType_name_4
|
||||
case i == 32:
|
||||
return _EvalType_name_5
|
||||
case i == 64:
|
||||
return _EvalType_name_6
|
||||
default:
|
||||
return fmt.Sprintf("EvalType(%d)", i)
|
||||
}
|
||||
|
|
|
@ -19,11 +19,13 @@ type ContextGraphWalker struct {
|
|||
// Outputs, do not set these. Do not read these while the graph
|
||||
// is being walked.
|
||||
EvalError error
|
||||
Diff *Diff
|
||||
ValidationWarnings []string
|
||||
ValidationErrors []error
|
||||
|
||||
errorLock sync.Mutex
|
||||
once sync.Once
|
||||
diffLock sync.RWMutex
|
||||
providerCache map[string]ResourceProvider
|
||||
providerConfigCache map[string]*ResourceConfig
|
||||
providerLock sync.Mutex
|
||||
|
@ -44,6 +46,8 @@ func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext {
|
|||
Provisioners: w.Context.provisioners,
|
||||
ProvisionerCache: w.provisionerCache,
|
||||
ProvisionerLock: &w.provisionerLock,
|
||||
DiffValue: w.Diff,
|
||||
DiffLock: &w.diffLock,
|
||||
StateValue: w.Context.state,
|
||||
StateLock: &w.Context.stateLock,
|
||||
Interpolater: &Interpolater{
|
||||
|
@ -87,6 +91,9 @@ func (w *ContextGraphWalker) ExitEvalTree(
|
|||
}
|
||||
|
||||
func (w *ContextGraphWalker) init() {
|
||||
w.Diff = new(Diff)
|
||||
w.Diff.init()
|
||||
|
||||
w.providerCache = make(map[string]ResourceProvider, 5)
|
||||
w.providerConfigCache = make(map[string]*ResourceConfig, 5)
|
||||
w.provisionerCache = make(map[string]ResourceProvisioner, 5)
|
||||
|
|
|
@ -115,6 +115,32 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
|||
},
|
||||
})
|
||||
|
||||
// Diff the resource
|
||||
var diff InstanceDiff
|
||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkPlan},
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &EvalDiff{
|
||||
Info: info,
|
||||
Config: &EvalInterpolate{Config: n.Resource.RawConfig},
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()[0]},
|
||||
State: &EvalReadState{Name: n.stateId()},
|
||||
Output: &diff,
|
||||
},
|
||||
},
|
||||
&EvalWriteDiff{
|
||||
Name: n.stateId(),
|
||||
Diff: &diff,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return seq
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue