core: [refactor] split WriteState EvalNodes
This is the non-DRY pass.
This commit is contained in:
parent
d81ec2d37e
commit
426f253085
|
@ -56,6 +56,9 @@ func (n *EvalReadStateDeposed) Eval(ctx EvalContext) (interface{}, error) {
|
|||
})
|
||||
}
|
||||
|
||||
// Does the bulk of the work for the various flavors of ReadState eval nodes.
|
||||
// Each node just provides a function to get from the ResourceState to the
|
||||
// InstanceState, and this takes care of all the plumbing.
|
||||
func readInstanceFromState(
|
||||
ctx EvalContext,
|
||||
resourceName string,
|
||||
|
@ -138,17 +141,12 @@ func (n *EvalUpdateStateHook) Eval(ctx EvalContext) (interface{}, error) {
|
|||
// 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 **InstanceState
|
||||
Tainted *bool
|
||||
TaintedIndex int
|
||||
TaintedClearPrimary bool
|
||||
Deposed bool
|
||||
Name string
|
||||
ResourceType string
|
||||
Dependencies []string
|
||||
State **InstanceState
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
|
||||
state, lock := ctx.State()
|
||||
if state == nil {
|
||||
|
@ -175,23 +173,120 @@ func (n *EvalWriteState) Eval(ctx EvalContext) (interface{}, error) {
|
|||
rs.Type = n.ResourceType
|
||||
rs.Dependencies = n.Dependencies
|
||||
|
||||
if n.Tainted != nil && *n.Tainted {
|
||||
if n.TaintedIndex != -1 {
|
||||
rs.Tainted[n.TaintedIndex] = *n.State
|
||||
} else {
|
||||
rs.Tainted = append(rs.Tainted, *n.State)
|
||||
}
|
||||
rs.Primary = *n.State
|
||||
|
||||
if n.TaintedClearPrimary {
|
||||
rs.Primary = nil
|
||||
}
|
||||
} else if n.Deposed {
|
||||
rs.Deposed = *n.State
|
||||
} else {
|
||||
// Set the primary state
|
||||
rs.Primary = *n.State
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type EvalWriteStateTainted struct {
|
||||
Name string
|
||||
ResourceType string
|
||||
Dependencies []string
|
||||
State **InstanceState
|
||||
TaintedIndex int
|
||||
}
|
||||
|
||||
func (n *EvalWriteStateTainted) Eval(ctx EvalContext) (interface{}, 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[n.Name]
|
||||
if rs == nil {
|
||||
rs = &ResourceState{}
|
||||
rs.init()
|
||||
mod.Resources[n.Name] = rs
|
||||
}
|
||||
rs.Type = n.ResourceType
|
||||
rs.Dependencies = n.Dependencies
|
||||
|
||||
if n.TaintedIndex == -1 {
|
||||
rs.Tainted = append(rs.Tainted, *n.State)
|
||||
} else {
|
||||
rs.Tainted[n.TaintedIndex] = *n.State
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type EvalWriteStateDeposed struct {
|
||||
Name string
|
||||
ResourceType string
|
||||
Dependencies []string
|
||||
State **InstanceState
|
||||
}
|
||||
|
||||
func (n *EvalWriteStateDeposed) Eval(ctx EvalContext) (interface{}, 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[n.Name]
|
||||
if rs == nil {
|
||||
rs = &ResourceState{}
|
||||
rs.init()
|
||||
mod.Resources[n.Name] = rs
|
||||
}
|
||||
rs.Type = n.ResourceType
|
||||
rs.Dependencies = n.Dependencies
|
||||
|
||||
rs.Deposed = *n.State
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -147,3 +147,79 @@ func TestEvalReadState(t *testing.T) {
|
|||
output = nil
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalWriteState(t *testing.T) {
|
||||
state := &State{}
|
||||
ctx := new(MockEvalContext)
|
||||
ctx.StateState = state
|
||||
ctx.StateLock = new(sync.RWMutex)
|
||||
ctx.PathPath = rootModulePath
|
||||
|
||||
is := &InstanceState{ID: "i-abc123"}
|
||||
node := &EvalWriteState{
|
||||
Name: "restype.resname",
|
||||
ResourceType: "restype",
|
||||
State: &is,
|
||||
}
|
||||
_, err := node.Eval(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Got err: %#v", err)
|
||||
}
|
||||
|
||||
rs := state.ModuleByPath(ctx.Path()).Resources["restype.resname"]
|
||||
if rs.Type != "restype" {
|
||||
t.Fatalf("expected type 'restype': %#v", rs)
|
||||
}
|
||||
if rs.Primary.ID != "i-abc123" {
|
||||
t.Fatalf("expected primary instance to have ID 'i-abc123': %#v", rs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalWriteStateTainted(t *testing.T) {
|
||||
state := &State{}
|
||||
ctx := new(MockEvalContext)
|
||||
ctx.StateState = state
|
||||
ctx.StateLock = new(sync.RWMutex)
|
||||
ctx.PathPath = rootModulePath
|
||||
|
||||
is := &InstanceState{ID: "i-abc123"}
|
||||
node := &EvalWriteStateTainted{
|
||||
Name: "restype.resname",
|
||||
ResourceType: "restype",
|
||||
State: &is,
|
||||
TaintedIndex: -1,
|
||||
}
|
||||
_, err := node.Eval(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Got err: %#v", err)
|
||||
}
|
||||
|
||||
rs := state.ModuleByPath(ctx.Path()).Resources["restype.resname"]
|
||||
if len(rs.Tainted) == 1 && rs.Tainted[0].ID != "i-abc123" {
|
||||
t.Fatalf("expected tainted instance to have ID 'i-abc123': %#v", rs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalWriteStateDeposed(t *testing.T) {
|
||||
state := &State{}
|
||||
ctx := new(MockEvalContext)
|
||||
ctx.StateState = state
|
||||
ctx.StateLock = new(sync.RWMutex)
|
||||
ctx.PathPath = rootModulePath
|
||||
|
||||
is := &InstanceState{ID: "i-abc123"}
|
||||
node := &EvalWriteStateDeposed{
|
||||
Name: "restype.resname",
|
||||
ResourceType: "restype",
|
||||
State: &is,
|
||||
}
|
||||
_, err := node.Eval(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Got err: %#v", err)
|
||||
}
|
||||
|
||||
rs := state.ModuleByPath(ctx.Path()).Resources["restype.resname"]
|
||||
if rs.Deposed == nil || rs.Deposed.ID != "i-abc123" {
|
||||
t.Fatalf("expected deposed instance to have ID 'i-abc123': %#v", rs)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ type DeposedTransformer struct {
|
|||
func (t *DeposedTransformer) Transform(g *Graph) error {
|
||||
state := t.State.ModuleByPath(g.Path)
|
||||
if state == nil {
|
||||
// If there is no state for our module there can't be any tainted
|
||||
// If there is no state for our module there can't be any deposed
|
||||
// resources, since they live in the state.
|
||||
return nil
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ func (t *DeposedTransformer) Transform(g *Graph) error {
|
|||
state = state.View(t.View)
|
||||
}
|
||||
|
||||
// Go through all the resources in our state to look for tainted resources
|
||||
// Go through all the resources in our state to look for deposed resources
|
||||
for k, rs := range state.Resources {
|
||||
if rs.Deposed == nil {
|
||||
continue
|
||||
|
@ -86,11 +86,10 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
|
|||
State: &state,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalWriteState{
|
||||
&EvalWriteStateDeposed{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
State: &state,
|
||||
Deposed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -100,7 +99,6 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
|
|||
var diff *InstanceDiff
|
||||
var err error
|
||||
var emptyState *InstanceState
|
||||
tainted := true
|
||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkApply},
|
||||
Node: &EvalSequence{
|
||||
|
@ -129,19 +127,17 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode {
|
|||
// Always write the resource back to the state tainted... if it
|
||||
// successfully destroyed it will be pruned. If it did not, it will
|
||||
// remain tainted.
|
||||
&EvalWriteState{
|
||||
&EvalWriteStateTainted{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
State: &state,
|
||||
Tainted: &tainted,
|
||||
TaintedIndex: -1,
|
||||
},
|
||||
// Then clear the deposed state.
|
||||
&EvalWriteState{
|
||||
&EvalWriteStateDeposed{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
State: &emptyState,
|
||||
Deposed: true,
|
||||
},
|
||||
&EvalReturnError{
|
||||
Error: &err,
|
||||
|
|
|
@ -395,14 +395,35 @@ func (n *graphNodeExpandedResource) EvalTree() EvalNode {
|
|||
Diff: nil,
|
||||
},
|
||||
|
||||
&EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
Tainted: &tainted,
|
||||
TaintedIndex: -1,
|
||||
TaintedClearPrimary: !n.Resource.Lifecycle.CreateBeforeDestroy,
|
||||
&EvalIf{
|
||||
If: func(ctx EvalContext) (bool, error) {
|
||||
return tainted, nil
|
||||
},
|
||||
Then: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalWriteStateTainted{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
TaintedIndex: -1,
|
||||
},
|
||||
&EvalIf{
|
||||
If: func(ctx EvalContext) (bool, error) {
|
||||
return !n.Resource.Lifecycle.CreateBeforeDestroy, nil
|
||||
},
|
||||
Then: &EvalClearPrimaryState{
|
||||
Name: n.stateId(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Else: &EvalWriteState{
|
||||
Name: n.stateId(),
|
||||
ResourceType: n.Resource.Type,
|
||||
Dependencies: n.DependentOn(),
|
||||
State: &state,
|
||||
},
|
||||
},
|
||||
&EvalApplyPost{
|
||||
Info: info,
|
||||
|
|
|
@ -71,7 +71,6 @@ func (n *graphNodeTaintedResource) ProvidedBy() []string {
|
|||
func (n *graphNodeTaintedResource) EvalTree() EvalNode {
|
||||
var provider ResourceProvider
|
||||
var state *InstanceState
|
||||
tainted := true
|
||||
|
||||
seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
|
||||
|
||||
|
@ -99,11 +98,10 @@ func (n *graphNodeTaintedResource) EvalTree() EvalNode {
|
|||
State: &state,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalWriteState{
|
||||
&EvalWriteStateTainted{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
State: &state,
|
||||
Tainted: &tainted,
|
||||
TaintedIndex: n.Index,
|
||||
},
|
||||
},
|
||||
|
@ -137,11 +135,10 @@ func (n *graphNodeTaintedResource) EvalTree() EvalNode {
|
|||
Provider: &provider,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalWriteState{
|
||||
&EvalWriteStateTainted{
|
||||
Name: n.ResourceName,
|
||||
ResourceType: n.ResourceType,
|
||||
State: &state,
|
||||
Tainted: &tainted,
|
||||
TaintedIndex: n.Index,
|
||||
},
|
||||
&EvalUpdateStateHook{},
|
||||
|
|
Loading…
Reference in New Issue