terraform: refactor NodeDestroyDeposedResourceInstanceObject and NodePlanDeposedResourceInstanceObject
The various Eval()s will be refactored in a later PR.
This commit is contained in:
parent
3bb64e80d5
commit
c66494b874
|
@ -6,7 +6,6 @@ import (
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/dag"
|
"github.com/hashicorp/terraform/dag"
|
||||||
"github.com/hashicorp/terraform/plans"
|
"github.com/hashicorp/terraform/plans"
|
||||||
"github.com/hashicorp/terraform/providers"
|
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,7 +36,7 @@ var (
|
||||||
_ GraphNodeResourceInstance = (*NodePlanDeposedResourceInstanceObject)(nil)
|
_ GraphNodeResourceInstance = (*NodePlanDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeReferenceable = (*NodePlanDeposedResourceInstanceObject)(nil)
|
_ GraphNodeReferenceable = (*NodePlanDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeReferencer = (*NodePlanDeposedResourceInstanceObject)(nil)
|
_ GraphNodeReferencer = (*NodePlanDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeEvalable = (*NodePlanDeposedResourceInstanceObject)(nil)
|
_ GraphNodeExecutable = (*NodePlanDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeProviderConsumer = (*NodePlanDeposedResourceInstanceObject)(nil)
|
_ GraphNodeProviderConsumer = (*NodePlanDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeProvisionerConsumer = (*NodePlanDeposedResourceInstanceObject)(nil)
|
_ GraphNodeProvisionerConsumer = (*NodePlanDeposedResourceInstanceObject)(nil)
|
||||||
)
|
)
|
||||||
|
@ -64,55 +63,58 @@ func (n *NodePlanDeposedResourceInstanceObject) References() []*addrs.Reference
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable impl.
|
// GraphNodeEvalable impl.
|
||||||
func (n *NodePlanDeposedResourceInstanceObject) EvalTree() EvalNode {
|
func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
addr := n.ResourceInstanceAddr()
|
addr := n.ResourceInstanceAddr()
|
||||||
|
|
||||||
var provider providers.Interface
|
provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
|
||||||
var providerSchema *ProviderSchema
|
if err != nil {
|
||||||
var state *states.ResourceInstanceObject
|
return err
|
||||||
|
}
|
||||||
seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
|
|
||||||
|
|
||||||
// During the plan walk we always produce a planned destroy change, because
|
// During the plan walk we always produce a planned destroy change, because
|
||||||
// destroying is the only supported action for deposed objects.
|
// destroying is the only supported action for deposed objects.
|
||||||
var change *plans.ResourceInstanceChange
|
var change *plans.ResourceInstanceChange
|
||||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
var state *states.ResourceInstanceObject
|
||||||
Ops: []walkOperation{walkPlan, walkPlanDestroy},
|
|
||||||
Node: &EvalSequence{
|
|
||||||
Nodes: []EvalNode{
|
|
||||||
&EvalGetProvider{
|
|
||||||
Addr: n.ResolvedProvider,
|
|
||||||
Output: &provider,
|
|
||||||
Schema: &providerSchema,
|
|
||||||
},
|
|
||||||
&EvalReadStateDeposed{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
Output: &state,
|
|
||||||
Key: n.DeposedKey,
|
|
||||||
Provider: &provider,
|
|
||||||
ProviderSchema: &providerSchema,
|
|
||||||
},
|
|
||||||
&EvalDiffDestroy{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
ProviderAddr: n.ResolvedProvider,
|
|
||||||
DeposedKey: n.DeposedKey,
|
|
||||||
State: &state,
|
|
||||||
Output: &change,
|
|
||||||
},
|
|
||||||
&EvalWriteDiff{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
DeposedKey: n.DeposedKey,
|
|
||||||
ProviderSchema: &providerSchema,
|
|
||||||
Change: &change,
|
|
||||||
},
|
|
||||||
// Since deposed objects cannot be referenced by expressions
|
|
||||||
// elsewhere, we don't need to also record the planned new
|
|
||||||
// state in this case.
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return seq
|
switch op {
|
||||||
|
case walkPlan, walkPlanDestroy:
|
||||||
|
|
||||||
|
readStateDeposed := &EvalReadStateDeposed{
|
||||||
|
Addr: addr.Resource,
|
||||||
|
Output: &state,
|
||||||
|
Key: n.DeposedKey,
|
||||||
|
Provider: &provider,
|
||||||
|
ProviderSchema: &providerSchema,
|
||||||
|
}
|
||||||
|
_, err = readStateDeposed.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
diffDestroy := &EvalDiffDestroy{
|
||||||
|
Addr: addr.Resource,
|
||||||
|
ProviderAddr: n.ResolvedProvider,
|
||||||
|
DeposedKey: n.DeposedKey,
|
||||||
|
State: &state,
|
||||||
|
Output: &change,
|
||||||
|
}
|
||||||
|
_, err = diffDestroy.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDiff := &EvalWriteDiff{
|
||||||
|
Addr: addr.Resource,
|
||||||
|
DeposedKey: n.DeposedKey,
|
||||||
|
ProviderSchema: &providerSchema,
|
||||||
|
Change: &change,
|
||||||
|
}
|
||||||
|
_, err = writeDiff.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeDestroyDeposedResourceInstanceObject represents deposed resource
|
// NodeDestroyDeposedResourceInstanceObject represents deposed resource
|
||||||
|
@ -133,7 +135,7 @@ var (
|
||||||
_ GraphNodeDestroyerCBD = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
_ GraphNodeDestroyerCBD = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeReferenceable = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
_ GraphNodeReferenceable = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeReferencer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
_ GraphNodeReferencer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeEvalable = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
_ GraphNodeExecutable = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeProviderConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
_ GraphNodeProviderConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
||||||
_ GraphNodeProvisionerConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
_ GraphNodeProvisionerConsumer = (*NodeDestroyDeposedResourceInstanceObject)(nil)
|
||||||
)
|
)
|
||||||
|
@ -181,74 +183,98 @@ func (n *NodeDestroyDeposedResourceInstanceObject) ModifyCreateBeforeDestroy(v b
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable impl.
|
// GraphNodeExecutable impl.
|
||||||
func (n *NodeDestroyDeposedResourceInstanceObject) EvalTree() EvalNode {
|
func (n *NodeDestroyDeposedResourceInstanceObject) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
addr := n.ResourceInstanceAddr()
|
addr := n.ResourceInstanceAddr().Resource
|
||||||
|
|
||||||
var provider providers.Interface
|
|
||||||
var providerSchema *ProviderSchema
|
|
||||||
var state *states.ResourceInstanceObject
|
var state *states.ResourceInstanceObject
|
||||||
var change *plans.ResourceInstanceChange
|
var change *plans.ResourceInstanceChange
|
||||||
var err error
|
var applyError error
|
||||||
|
|
||||||
return &EvalSequence{
|
provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
|
||||||
Nodes: []EvalNode{
|
if err != nil {
|
||||||
&EvalGetProvider{
|
return err
|
||||||
Addr: n.ResolvedProvider,
|
|
||||||
Output: &provider,
|
|
||||||
Schema: &providerSchema,
|
|
||||||
},
|
|
||||||
&EvalReadStateDeposed{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
Output: &state,
|
|
||||||
Key: n.DeposedKey,
|
|
||||||
Provider: &provider,
|
|
||||||
ProviderSchema: &providerSchema,
|
|
||||||
},
|
|
||||||
&EvalDiffDestroy{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
ProviderAddr: n.ResolvedProvider,
|
|
||||||
State: &state,
|
|
||||||
Output: &change,
|
|
||||||
},
|
|
||||||
// Call pre-apply hook
|
|
||||||
&EvalApplyPre{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
State: &state,
|
|
||||||
Change: &change,
|
|
||||||
},
|
|
||||||
&EvalApply{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
Config: nil, // No configuration because we are destroying
|
|
||||||
State: &state,
|
|
||||||
Change: &change,
|
|
||||||
Provider: &provider,
|
|
||||||
ProviderAddr: n.ResolvedProvider,
|
|
||||||
ProviderSchema: &providerSchema,
|
|
||||||
Output: &state,
|
|
||||||
Error: &err,
|
|
||||||
},
|
|
||||||
// Always write the resource back to the state deposed... if it
|
|
||||||
// was successfully destroyed it will be pruned. If it was not, it will
|
|
||||||
// be caught on the next run.
|
|
||||||
&EvalWriteStateDeposed{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
Key: n.DeposedKey,
|
|
||||||
ProviderAddr: n.ResolvedProvider,
|
|
||||||
ProviderSchema: &providerSchema,
|
|
||||||
State: &state,
|
|
||||||
},
|
|
||||||
&EvalApplyPost{
|
|
||||||
Addr: addr.Resource,
|
|
||||||
State: &state,
|
|
||||||
Error: &err,
|
|
||||||
},
|
|
||||||
&EvalReturnError{
|
|
||||||
Error: &err,
|
|
||||||
},
|
|
||||||
&EvalUpdateStateHook{},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readStateDeposed := &EvalReadStateDeposed{
|
||||||
|
Addr: addr,
|
||||||
|
Output: &state,
|
||||||
|
Key: n.DeposedKey,
|
||||||
|
Provider: &provider,
|
||||||
|
ProviderSchema: &providerSchema,
|
||||||
|
}
|
||||||
|
_, err = readStateDeposed.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
diffDestroy := &EvalDiffDestroy{
|
||||||
|
Addr: addr,
|
||||||
|
ProviderAddr: n.ResolvedProvider,
|
||||||
|
State: &state,
|
||||||
|
Output: &change,
|
||||||
|
}
|
||||||
|
_, err = diffDestroy.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call pre-apply hook
|
||||||
|
applyPre := &EvalApplyPre{
|
||||||
|
Addr: addr,
|
||||||
|
State: &state,
|
||||||
|
Change: &change,
|
||||||
|
}
|
||||||
|
_, err = applyPre.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
apply := &EvalApply{
|
||||||
|
Addr: addr,
|
||||||
|
Config: nil, // No configuration because we are destroying
|
||||||
|
State: &state,
|
||||||
|
Change: &change,
|
||||||
|
Provider: &provider,
|
||||||
|
ProviderAddr: n.ResolvedProvider,
|
||||||
|
ProviderSchema: &providerSchema,
|
||||||
|
Output: &state,
|
||||||
|
Error: &applyError,
|
||||||
|
}
|
||||||
|
_, err = apply.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always write the resource back to the state deposed. If it
|
||||||
|
// was successfully destroyed it will be pruned. If it was not, it will
|
||||||
|
// be caught on the next run.
|
||||||
|
writeStateDeposed := &EvalWriteStateDeposed{
|
||||||
|
Addr: addr,
|
||||||
|
Key: n.DeposedKey,
|
||||||
|
ProviderAddr: n.ResolvedProvider,
|
||||||
|
ProviderSchema: &providerSchema,
|
||||||
|
State: &state,
|
||||||
|
}
|
||||||
|
_, err = writeStateDeposed.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPost := &EvalApplyPost{
|
||||||
|
Addr: addr,
|
||||||
|
State: &state,
|
||||||
|
Error: &applyError,
|
||||||
|
}
|
||||||
|
_, err = applyPost.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if applyError != nil {
|
||||||
|
return applyError
|
||||||
|
}
|
||||||
|
UpdateStateHook(ctx)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeDeposer is an optional interface implemented by graph nodes that
|
// GraphNodeDeposer is an optional interface implemented by graph nodes that
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
package terraform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
"github.com/hashicorp/terraform/configs/configschema"
|
||||||
|
"github.com/hashicorp/terraform/plans"
|
||||||
|
"github.com/hashicorp/terraform/providers"
|
||||||
|
"github.com/hashicorp/terraform/states"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNodePlanDeposedResourceInstanceObject_Execute(t *testing.T) {
|
||||||
|
deposedKey := states.NewDeposedKey()
|
||||||
|
state := states.NewState()
|
||||||
|
absResource := mustResourceInstanceAddr("test_instance.foo")
|
||||||
|
state.Module(addrs.RootModuleInstance).SetResourceInstanceDeposed(
|
||||||
|
absResource.Resource,
|
||||||
|
deposedKey,
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectTainted,
|
||||||
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||||
|
},
|
||||||
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
||||||
|
)
|
||||||
|
|
||||||
|
p := testProvider("test")
|
||||||
|
p.UpgradeResourceStateResponse = providers.UpgradeResourceStateResponse{
|
||||||
|
UpgradedState: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("bar"),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
ctx := &MockEvalContext{
|
||||||
|
StateState: state.SyncWrapper(),
|
||||||
|
ProviderProvider: p,
|
||||||
|
ProviderSchemaSchema: &ProviderSchema{
|
||||||
|
ResourceTypes: map[string]*configschema.Block{
|
||||||
|
"test_instance": {
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"id": {
|
||||||
|
Type: cty.String,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ChangesChanges: plans.NewChanges().SyncWrapper(),
|
||||||
|
}
|
||||||
|
|
||||||
|
node := NodePlanDeposedResourceInstanceObject{
|
||||||
|
NodeAbstractResourceInstance: &NodeAbstractResourceInstance{
|
||||||
|
Addr: absResource,
|
||||||
|
NodeAbstractResource: NodeAbstractResource{
|
||||||
|
ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DeposedKey: deposedKey,
|
||||||
|
}
|
||||||
|
err := node.Execute(ctx, walkPlan)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
change := ctx.Changes().GetResourceInstanceChange(absResource, deposedKey)
|
||||||
|
if change.ChangeSrc.Action != plans.Delete {
|
||||||
|
t.Fatalf("delete change not planned")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeDestroyDeposedResourceInstanceObject_Execute(t *testing.T) {
|
||||||
|
deposedKey := states.NewDeposedKey()
|
||||||
|
state := states.NewState()
|
||||||
|
absResource := mustResourceInstanceAddr("test_instance.foo")
|
||||||
|
state.Module(addrs.RootModuleInstance).SetResourceInstanceDeposed(
|
||||||
|
absResource.Resource,
|
||||||
|
deposedKey,
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
Status: states.ObjectTainted,
|
||||||
|
AttrsJSON: []byte(`{"id":"bar"}`),
|
||||||
|
},
|
||||||
|
mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
||||||
|
)
|
||||||
|
|
||||||
|
p := testProvider("test")
|
||||||
|
p.UpgradeResourceStateResponse = providers.UpgradeResourceStateResponse{
|
||||||
|
UpgradedState: cty.ObjectVal(map[string]cty.Value{
|
||||||
|
"id": cty.StringVal("bar"),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
ctx := &MockEvalContext{
|
||||||
|
StateState: state.SyncWrapper(),
|
||||||
|
ProviderProvider: p,
|
||||||
|
ProviderSchemaSchema: &ProviderSchema{
|
||||||
|
ResourceTypes: map[string]*configschema.Block{
|
||||||
|
"test_instance": {
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"id": {
|
||||||
|
Type: cty.String,
|
||||||
|
Computed: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ChangesChanges: plans.NewChanges().SyncWrapper(),
|
||||||
|
}
|
||||||
|
|
||||||
|
node := NodeDestroyDeposedResourceInstanceObject{
|
||||||
|
NodeAbstractResourceInstance: &NodeAbstractResourceInstance{
|
||||||
|
Addr: absResource,
|
||||||
|
NodeAbstractResource: NodeAbstractResource{
|
||||||
|
ResolvedProvider: mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DeposedKey: deposedKey,
|
||||||
|
}
|
||||||
|
err := node.Execute(ctx, walkApply)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !state.Empty() {
|
||||||
|
t.Fatalf("resources left in state after destroy")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue