terraform: make things more linear
This commit is contained in:
parent
67e7aeeea0
commit
93f3050dbd
|
@ -81,6 +81,24 @@ func (c *Context2) GraphBuilder() GraphBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
// Apply applies the changes represented by this context and returns
|
||||
// the resulting state.
|
||||
//
|
||||
// In addition to returning the resulting state, this context is updated
|
||||
// with the latest state.
|
||||
func (c *Context2) Apply() (*State, error) {
|
||||
// Copy our own state
|
||||
c.state = c.state.deepcopy()
|
||||
|
||||
// Do the walk
|
||||
_, err := c.walk(walkApply)
|
||||
|
||||
// Clean out any unused things
|
||||
c.state.prune()
|
||||
|
||||
return c.state, err
|
||||
}
|
||||
|
||||
// Plan generates an execution plan for the given context.
|
||||
//
|
||||
// The execution plan encapsulates the context and can be stored
|
||||
|
|
|
@ -2671,13 +2671,15 @@ func TestContextInput_varOnly(t *testing.T) {
|
|||
t.Fatalf("bad: \n%s", actualStr)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestContextApply(t *testing.T) {
|
||||
/*
|
||||
func TestContext2Apply(t *testing.T) {
|
||||
m := testModule(t, "apply-good")
|
||||
p := testProvider("aws")
|
||||
p.ApplyFn = testApplyFn
|
||||
p.DiffFn = testDiffFn
|
||||
ctx := testContext(t, &ContextOpts{
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Module: m,
|
||||
Providers: map[string]ResourceProviderFactory{
|
||||
"aws": testProviderFuncFixed(p),
|
||||
|
@ -2705,6 +2707,7 @@ func TestContextApply(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func TestContextApply_emptyModule(t *testing.T) {
|
||||
m := testModule(t, "apply-empty-module")
|
||||
p := testProvider("aws")
|
||||
|
|
|
@ -3,11 +3,12 @@ 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
|
||||
Info *InstanceInfo
|
||||
Config EvalNode
|
||||
Provider EvalNode
|
||||
State EvalNode
|
||||
Output **InstanceDiff
|
||||
OutputState **InstanceState
|
||||
}
|
||||
|
||||
func (n *EvalDiff) Args() ([]EvalNode, []EvalType) {
|
||||
|
@ -82,11 +83,12 @@ func (n *EvalDiff) Eval(
|
|||
}
|
||||
|
||||
// Update our output
|
||||
*n.Output = *diff
|
||||
*n.Output = diff
|
||||
*n.OutputState = state
|
||||
|
||||
// Merge our state so that the state is updated with our plan
|
||||
if !diff.Empty() {
|
||||
state = state.MergeDiff(diff)
|
||||
if !diff.Empty() && n.OutputState != nil {
|
||||
*n.OutputState = state.MergeDiff(diff)
|
||||
}
|
||||
|
||||
return state, nil
|
||||
|
@ -101,7 +103,7 @@ func (n *EvalDiff) Type() EvalType {
|
|||
type EvalDiffDestroy struct {
|
||||
Info *InstanceInfo
|
||||
State EvalNode
|
||||
Output *InstanceDiff
|
||||
Output **InstanceDiff
|
||||
}
|
||||
|
||||
func (n *EvalDiffDestroy) Args() ([]EvalNode, []EvalType) {
|
||||
|
@ -142,7 +144,7 @@ func (n *EvalDiffDestroy) Eval(
|
|||
}
|
||||
|
||||
// Update our output
|
||||
*n.Output = *diff
|
||||
*n.Output = diff
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -188,7 +190,7 @@ func (n *EvalDiffDestroyModule) Type() EvalType {
|
|||
// the full diff.
|
||||
type EvalDiffTainted struct {
|
||||
Name string
|
||||
Diff *InstanceDiff
|
||||
Diff **InstanceDiff
|
||||
}
|
||||
|
||||
func (n *EvalDiffTainted) Args() ([]EvalNode, []EvalType) {
|
||||
|
@ -218,7 +220,7 @@ func (n *EvalDiffTainted) Eval(
|
|||
|
||||
// If we have tainted, then mark it on the diff
|
||||
if len(rs.Tainted) > 0 {
|
||||
n.Diff.DestroyTainted = true
|
||||
(*n.Diff).DestroyTainted = true
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
@ -228,11 +230,46 @@ func (n *EvalDiffTainted) Type() EvalType {
|
|||
return EvalTypeNull
|
||||
}
|
||||
|
||||
// EvalReadDiff is an EvalNode implementation that writes the diff to
|
||||
// the full diff.
|
||||
type EvalReadDiff struct {
|
||||
Name string
|
||||
Diff **InstanceDiff
|
||||
}
|
||||
|
||||
func (n *EvalReadDiff) Args() ([]EvalNode, []EvalType) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalReadDiff) 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 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
*n.Diff = modDiff.Resources[n.Name]
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (n *EvalReadDiff) Type() EvalType {
|
||||
return EvalTypeNull
|
||||
}
|
||||
|
||||
// EvalWriteDiff is an EvalNode implementation that writes the diff to
|
||||
// the full diff.
|
||||
type EvalWriteDiff struct {
|
||||
Name string
|
||||
Diff *InstanceDiff
|
||||
Diff **InstanceDiff
|
||||
}
|
||||
|
||||
func (n *EvalWriteDiff) Args() ([]EvalNode, []EvalType) {
|
||||
|
@ -245,7 +282,7 @@ func (n *EvalWriteDiff) Eval(
|
|||
diff, lock := ctx.Diff()
|
||||
|
||||
// The diff to write, if its empty it should write nil
|
||||
diffVal := n.Diff
|
||||
diffVal := *n.Diff
|
||||
if diffVal.Empty() {
|
||||
diffVal = nil
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ func (n *EvalInitProvider) Type() EvalType {
|
|||
// EvalGetProvider is an EvalNode implementation that retrieves an already
|
||||
// initialized provider instance for the given name.
|
||||
type EvalGetProvider struct {
|
||||
Name string
|
||||
Name string
|
||||
Output *ResourceProvider
|
||||
}
|
||||
|
||||
func (n *EvalGetProvider) Args() ([]EvalNode, []EvalType) {
|
||||
|
@ -69,6 +70,10 @@ func (n *EvalGetProvider) Eval(
|
|||
return nil, fmt.Errorf("provider %s not initialized", n.Name)
|
||||
}
|
||||
|
||||
if n.Output != nil {
|
||||
*n.Output = result
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
// EvalRefresh is an EvalNode implementation that does a refresh for
|
||||
// a resource.
|
||||
type EvalRefresh struct {
|
||||
Provider EvalNode
|
||||
State EvalNode
|
||||
State **InstanceState
|
||||
Info *InstanceInfo
|
||||
Output **InstanceState
|
||||
}
|
||||
|
||||
func (n *EvalRefresh) Args() ([]EvalNode, []EvalType) {
|
||||
return []EvalNode{n.Provider, n.State},
|
||||
[]EvalType{EvalTypeResourceProvider, EvalTypeInstanceState}
|
||||
return []EvalNode{n.Provider}, []EvalType{EvalTypeResourceProvider}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
state := *n.State
|
||||
|
||||
// If we have no state, we don't do any refreshing
|
||||
if state == nil {
|
||||
log.Printf("[DEBUG] refresh: %s: no state, not refreshing", n.Info.Id)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -49,6 +51,9 @@ func (n *EvalRefresh) Eval(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if n.Output != nil {
|
||||
*n.Output = state
|
||||
}
|
||||
return state, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ type EvalReadState struct {
|
|||
Name string
|
||||
Tainted bool
|
||||
TaintedIndex int
|
||||
Output **InstanceState
|
||||
}
|
||||
|
||||
func (n *EvalReadState) Args() ([]EvalNode, []EvalType) {
|
||||
|
@ -37,13 +38,21 @@ func (n *EvalReadState) Eval(
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
var result *InstanceState
|
||||
if !n.Tainted {
|
||||
// Return the primary
|
||||
return rs.Primary, nil
|
||||
result = rs.Primary
|
||||
} else {
|
||||
// Return the proper tainted resource
|
||||
return rs.Tainted[n.TaintedIndex], nil
|
||||
result = rs.Tainted[n.TaintedIndex]
|
||||
}
|
||||
|
||||
// Write the result to the output pointer
|
||||
if n.Output != nil {
|
||||
*n.Output = result
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (n *EvalReadState) Type() EvalType {
|
||||
|
@ -56,23 +65,18 @@ type EvalWriteState struct {
|
|||
Name string
|
||||
ResourceType string
|
||||
Dependencies []string
|
||||
State EvalNode
|
||||
State **InstanceState
|
||||
Tainted bool
|
||||
TaintedIndex int
|
||||
}
|
||||
|
||||
func (n *EvalWriteState) Args() ([]EvalNode, []EvalType) {
|
||||
return []EvalNode{n.State}, []EvalType{EvalTypeInstanceState}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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")
|
||||
|
@ -100,11 +104,11 @@ func (n *EvalWriteState) Eval(
|
|||
|
||||
if n.Tainted {
|
||||
if n.TaintedIndex != -1 {
|
||||
rs.Tainted[n.TaintedIndex] = instanceState
|
||||
rs.Tainted[n.TaintedIndex] = *n.State
|
||||
}
|
||||
} else {
|
||||
// Set the primary state
|
||||
rs.Primary = instanceState
|
||||
rs.Primary = *n.State
|
||||
}
|
||||
|
||||
// Prune because why not, we can clear out old useless entries now
|
||||
|
|
|
@ -550,7 +550,7 @@ func (r *ResourceState) prune() {
|
|||
n := len(r.Tainted)
|
||||
for i := 0; i < n; i++ {
|
||||
inst := r.Tainted[i]
|
||||
if inst.ID == "" {
|
||||
if inst == nil || inst.ID == "" {
|
||||
copy(r.Tainted[i:], r.Tainted[i+1:])
|
||||
r.Tainted[n-1] = nil
|
||||
n--
|
||||
|
|
|
@ -171,6 +171,8 @@ func (n *graphNodeOrphanResource) ProvidedBy() []string {
|
|||
|
||||
// GraphNodeEvalable impl.
|
||||
func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
||||
var state *InstanceState
|
||||
|
||||
seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
|
||||
|
||||
// Build instance info
|
||||
|
@ -180,22 +182,30 @@ func (n *graphNodeOrphanResource) EvalTree() EvalNode {
|
|||
// Refresh the resource
|
||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkRefresh},
|
||||
Node: &EvalWriteState{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &EvalRefresh{
|
||||
Info: info,
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()[0]},
|
||||
State: &EvalReadState{
|
||||
Name: n.ResourceName,
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalReadState{
|
||||
Name: n.ResourceName,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalRefresh{
|
||||
Info: info,
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()[0]},
|
||||
State: &state,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalWriteState{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Diff the resource
|
||||
var diff InstanceDiff
|
||||
var diff *InstanceDiff
|
||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkPlan, walkPlanDestroy},
|
||||
Node: &EvalSequence{
|
||||
|
|
|
@ -98,6 +98,9 @@ func (n *graphNodeExpandedResource) ProvidedBy() []string {
|
|||
|
||||
// GraphNodeEvalable impl.
|
||||
func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
||||
var diff *InstanceDiff
|
||||
var state *InstanceState
|
||||
|
||||
// Build the resource. If we aren't part of a multi-resource, then
|
||||
// we still consider ourselves as count index zero.
|
||||
index := n.Index
|
||||
|
@ -145,35 +148,46 @@ 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{
|
||||
Info: info,
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()[0]},
|
||||
State: &EvalReadState{Name: n.stateId()},
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalReadState{
|
||||
Name: n.stateId(),
|
||||
Output: &state,
|
||||
},
|
||||
&EvalRefresh{
|
||||
Info: info,
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()[0]},
|
||||
State: &state,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// Diff the resource
|
||||
var diff InstanceDiff
|
||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkPlan},
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalDiff{
|
||||
Info: info,
|
||||
Config: interpolateNode,
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()[0]},
|
||||
State: &EvalReadState{Name: n.stateId()},
|
||||
Output: &diff,
|
||||
OutputState: &state,
|
||||
},
|
||||
&EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &EvalDiff{
|
||||
Info: info,
|
||||
Config: interpolateNode,
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()[0]},
|
||||
State: &EvalReadState{Name: n.stateId()},
|
||||
Output: &diff,
|
||||
},
|
||||
State: &state,
|
||||
},
|
||||
&EvalDiffTainted{
|
||||
Diff: &diff,
|
||||
|
@ -205,6 +219,41 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
|||
},
|
||||
})
|
||||
|
||||
// Diff the resource for destruction
|
||||
var provider ResourceProvider
|
||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkApply},
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalGetProvider{
|
||||
Name: n.ProvidedBy()[0],
|
||||
Output: &provider,
|
||||
},
|
||||
&EvalReadDiff{
|
||||
Name: n.stateId(),
|
||||
Diff: &diff,
|
||||
},
|
||||
&EvalReadState{
|
||||
Name: n.stateId(),
|
||||
Output: &state,
|
||||
},
|
||||
&EvalApply{
|
||||
Info: info,
|
||||
State: &state,
|
||||
Diff: &diff,
|
||||
Provider: &provider,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
return seq
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ func (n *graphNodeTaintedResource) ProvidedBy() []string {
|
|||
|
||||
// GraphNodeEvalable impl.
|
||||
func (n *graphNodeTaintedResource) EvalTree() EvalNode {
|
||||
var state *InstanceState
|
||||
|
||||
seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
|
||||
|
||||
// Build instance info
|
||||
|
@ -74,19 +76,27 @@ func (n *graphNodeTaintedResource) EvalTree() EvalNode {
|
|||
// Refresh the resource
|
||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkRefresh},
|
||||
Node: &EvalWriteState{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Dependencies: n.DependentOn(),
|
||||
Tainted: true,
|
||||
TaintedIndex: n.Index,
|
||||
State: &EvalRefresh{
|
||||
Info: info,
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()[0]},
|
||||
State: &EvalReadState{
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalReadState{
|
||||
Name: n.ResourceName,
|
||||
Tainted: true,
|
||||
TaintedIndex: n.Index,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalRefresh{
|
||||
Info: info,
|
||||
Provider: &EvalGetProvider{Name: n.ProvidedBy()[0]},
|
||||
State: &state,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalWriteState{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
Tainted: true,
|
||||
TaintedIndex: n.Index,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue