evaltree refactor
This commit is contained in:
parent
c66494b874
commit
184893d1e4
|
@ -498,6 +498,8 @@ func (n *NodeAbstractResource) WriteResourceState(ctx EvalContext, addr addrs.Ab
|
|||
return nil
|
||||
}
|
||||
|
||||
// ReadResourceInstanceState reads the current object for a specific instance in
|
||||
// the state.
|
||||
func (n *NodeAbstractResource) ReadResourceInstanceState(ctx EvalContext, addr addrs.AbsResourceInstance) (*states.ResourceInstanceObject, error) {
|
||||
provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
|
||||
|
||||
|
@ -540,31 +542,6 @@ func (n *NodeAbstractResource) ReadResourceInstanceState(ctx EvalContext, addr a
|
|||
return obj, nil
|
||||
}
|
||||
|
||||
// CheckPreventDestroy returns an error if a resource has PreventDestroy
|
||||
// configured and the diff would destroy the resource.
|
||||
func (n *NodeAbstractResource) CheckPreventDestroy(addr addrs.AbsResourceInstance, change *plans.ResourceInstanceChange) error {
|
||||
if change == nil || n.Config == nil || n.Config.Managed == nil {
|
||||
return nil
|
||||
}
|
||||
preventDestroy := n.Config.Managed.PreventDestroy
|
||||
|
||||
if (change.Action == plans.Delete || change.Action.IsReplace()) && preventDestroy {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Instance cannot be destroyed",
|
||||
Detail: fmt.Sprintf(
|
||||
"Resource %s has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.",
|
||||
addr,
|
||||
),
|
||||
Subject: &n.Config.DeclRange,
|
||||
})
|
||||
return diags.Err()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadDiff returns the planned change for a particular resource instance
|
||||
// object.
|
||||
func (n *NodeAbstractResourceInstance) ReadDiff(ctx EvalContext, providerSchema *ProviderSchema) (*plans.ResourceInstanceChange, error) {
|
||||
|
@ -594,6 +571,30 @@ func (n *NodeAbstractResourceInstance) ReadDiff(ctx EvalContext, providerSchema
|
|||
return change, nil
|
||||
}
|
||||
|
||||
func (n *NodeAbstractResourceInstance) checkPreventDestroy(change *plans.ResourceInstanceChange) error {
|
||||
if change == nil || n.Config == nil || n.Config.Managed == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
preventDestroy := n.Config.Managed.PreventDestroy
|
||||
|
||||
if (change.Action == plans.Delete || change.Action.IsReplace()) && preventDestroy {
|
||||
var diags tfdiags.Diagnostics
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Instance cannot be destroyed",
|
||||
Detail: fmt.Sprintf(
|
||||
"Resource %s has lifecycle.prevent_destroy set, but the plan calls for this resource to be destroyed. To avoid this error and continue with the plan, either disable lifecycle.prevent_destroy or reduce the scope of the plan using the -target flag.",
|
||||
n.Addr.String(),
|
||||
),
|
||||
Subject: &n.Config.DeclRange,
|
||||
})
|
||||
return diags.Err()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// graphNodesAreResourceInstancesInDifferentInstancesOfSameModule is an
|
||||
// annoyingly-task-specific helper function that returns true if and only if
|
||||
// the following conditions hold:
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
"github.com/hashicorp/terraform/plans"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
)
|
||||
|
||||
|
@ -25,7 +21,7 @@ var (
|
|||
_ GraphNodeResourceInstance = (*NodePlanDestroyableResourceInstance)(nil)
|
||||
_ GraphNodeAttachResourceConfig = (*NodePlanDestroyableResourceInstance)(nil)
|
||||
_ GraphNodeAttachResourceState = (*NodePlanDestroyableResourceInstance)(nil)
|
||||
_ GraphNodeEvalable = (*NodePlanDestroyableResourceInstance)(nil)
|
||||
_ GraphNodeExecutable = (*NodePlanDestroyableResourceInstance)(nil)
|
||||
_ GraphNodeProviderConsumer = (*NodePlanDestroyableResourceInstance)(nil)
|
||||
)
|
||||
|
||||
|
@ -36,53 +32,46 @@ func (n *NodePlanDestroyableResourceInstance) DestroyAddr() *addrs.AbsResourceIn
|
|||
}
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodePlanDestroyableResourceInstance) EvalTree() EvalNode {
|
||||
func (n *NodePlanDestroyableResourceInstance) Execute(ctx EvalContext, op walkOperation) error {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
// Declare a bunch of variables that are used for state during
|
||||
// evaluation. These are written to by address in the EvalNodes we
|
||||
// declare below.
|
||||
var provider providers.Interface
|
||||
var providerSchema *ProviderSchema
|
||||
var change *plans.ResourceInstanceChange
|
||||
var state *states.ResourceInstanceObject
|
||||
|
||||
if n.ResolvedProvider.Provider.Type == "" {
|
||||
// Should never happen; indicates that the graph was not constructed
|
||||
// correctly since we didn't get our provider attached.
|
||||
panic(fmt.Sprintf("%T %q was not assigned a resolved provider", n, dag.VertexName(n)))
|
||||
_, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalGetProvider{
|
||||
Addr: n.ResolvedProvider,
|
||||
Output: &provider,
|
||||
Schema: &providerSchema,
|
||||
},
|
||||
&EvalReadState{
|
||||
Addr: addr.Resource,
|
||||
Provider: &provider,
|
||||
ProviderSchema: &providerSchema,
|
||||
|
||||
Output: &state,
|
||||
},
|
||||
&EvalDiffDestroy{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
State: &state,
|
||||
Output: &change,
|
||||
},
|
||||
&EvalCheckPreventDestroy{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
Change: &change,
|
||||
},
|
||||
&EvalWriteDiff{
|
||||
Addr: addr.Resource,
|
||||
ProviderSchema: &providerSchema,
|
||||
Change: &change,
|
||||
},
|
||||
},
|
||||
state, err = n.ReadResourceInstanceState(ctx, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
diffDestroy := &EvalDiffDestroy{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
State: &state,
|
||||
Output: &change,
|
||||
}
|
||||
_, err = diffDestroy.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = n.checkPreventDestroy(change)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeDiff := &EvalWriteDiff{
|
||||
Addr: addr.Resource,
|
||||
ProviderSchema: &providerSchema,
|
||||
Change: &change,
|
||||
}
|
||||
_, err = writeDiff.Eval(ctx)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/plans"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
|
@ -27,183 +26,211 @@ var (
|
|||
_ GraphNodeResourceInstance = (*NodePlannableResourceInstance)(nil)
|
||||
_ GraphNodeAttachResourceConfig = (*NodePlannableResourceInstance)(nil)
|
||||
_ GraphNodeAttachResourceState = (*NodePlannableResourceInstance)(nil)
|
||||
_ GraphNodeEvalable = (*NodePlannableResourceInstance)(nil)
|
||||
_ GraphNodeExecutable = (*NodePlannableResourceInstance)(nil)
|
||||
)
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodePlannableResourceInstance) EvalTree() EvalNode {
|
||||
func (n *NodePlannableResourceInstance) Execute(ctx EvalContext, op walkOperation) error {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
// Eval info is different depending on what kind of resource this is
|
||||
switch addr.Resource.Resource.Mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
return n.evalTreeManagedResource(addr)
|
||||
return n.managedResourceExecute(ctx, n.skipRefresh)
|
||||
case addrs.DataResourceMode:
|
||||
return n.evalTreeDataResource(addr)
|
||||
return n.dataResourceExecute(ctx)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NodePlannableResourceInstance) evalTreeDataResource(addr addrs.AbsResourceInstance) EvalNode {
|
||||
func (n *NodePlannableResourceInstance) dataResourceExecute(ctx EvalContext) error {
|
||||
config := n.Config
|
||||
var provider providers.Interface
|
||||
var providerSchema *ProviderSchema
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
var change *plans.ResourceInstanceChange
|
||||
var state *states.ResourceInstanceObject
|
||||
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalGetProvider{
|
||||
Addr: n.ResolvedProvider,
|
||||
Output: &provider,
|
||||
Schema: &providerSchema,
|
||||
},
|
||||
provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
&EvalReadState{
|
||||
Addr: addr.Resource,
|
||||
Provider: &provider,
|
||||
ProviderSchema: &providerSchema,
|
||||
Output: &state,
|
||||
},
|
||||
state, err = n.ReadResourceInstanceState(ctx, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
&EvalValidateSelfRef{
|
||||
Addr: addr.Resource,
|
||||
Config: config.Config,
|
||||
ProviderSchema: &providerSchema,
|
||||
},
|
||||
validateSelfRef := &EvalValidateSelfRef{
|
||||
Addr: addr.Resource,
|
||||
Config: config.Config,
|
||||
ProviderSchema: &providerSchema,
|
||||
}
|
||||
_, err = validateSelfRef.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
&evalReadDataPlan{
|
||||
evalReadData: evalReadData{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
Provider: &provider,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
OutputChange: &change,
|
||||
State: &state,
|
||||
dependsOn: n.dependsOn,
|
||||
},
|
||||
},
|
||||
|
||||
// write the data source into both the refresh state and the
|
||||
// working state
|
||||
&EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
targetState: refreshState,
|
||||
},
|
||||
|
||||
&EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
},
|
||||
|
||||
&EvalWriteDiff{
|
||||
Addr: addr.Resource,
|
||||
ProviderSchema: &providerSchema,
|
||||
Change: &change,
|
||||
},
|
||||
readDataPlan := &evalReadDataPlan{
|
||||
evalReadData: evalReadData{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
Provider: &provider,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
OutputChange: &change,
|
||||
State: &state,
|
||||
dependsOn: n.dependsOn,
|
||||
},
|
||||
}
|
||||
_, err = readDataPlan.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// write the data source into both the refresh state and the
|
||||
// working state
|
||||
writeRefreshState := &EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
targetState: refreshState,
|
||||
}
|
||||
_, err = writeRefreshState.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeState := &EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
}
|
||||
_, err = writeState.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeDiff := &EvalWriteDiff{
|
||||
Addr: addr.Resource,
|
||||
ProviderSchema: &providerSchema,
|
||||
Change: &change,
|
||||
}
|
||||
_, err = writeDiff.Eval(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
func (n *NodePlannableResourceInstance) evalTreeManagedResource(addr addrs.AbsResourceInstance) EvalNode {
|
||||
func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext, skipRefresh bool) error {
|
||||
config := n.Config
|
||||
var provider providers.Interface
|
||||
var providerSchema *ProviderSchema
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
var change *plans.ResourceInstanceChange
|
||||
var instanceRefreshState *states.ResourceInstanceObject
|
||||
var instancePlanState *states.ResourceInstanceObject
|
||||
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalGetProvider{
|
||||
Addr: n.ResolvedProvider,
|
||||
Output: &provider,
|
||||
Schema: &providerSchema,
|
||||
},
|
||||
|
||||
&EvalValidateSelfRef{
|
||||
Addr: addr.Resource,
|
||||
Config: config.Config,
|
||||
ProviderSchema: &providerSchema,
|
||||
},
|
||||
|
||||
&EvalIf{
|
||||
If: func(ctx EvalContext) (bool, error) {
|
||||
return !n.skipRefresh, nil
|
||||
},
|
||||
Then: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
// Refresh the instance
|
||||
&EvalReadState{
|
||||
Addr: addr.Resource,
|
||||
Provider: &provider,
|
||||
ProviderSchema: &providerSchema,
|
||||
Output: &instanceRefreshState,
|
||||
},
|
||||
&EvalRefreshLifecycle{
|
||||
Addr: addr,
|
||||
Config: n.Config,
|
||||
State: &instanceRefreshState,
|
||||
ForceCreateBeforeDestroy: n.ForceCreateBeforeDestroy,
|
||||
},
|
||||
&EvalRefresh{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
Provider: &provider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &instanceRefreshState,
|
||||
Output: &instanceRefreshState,
|
||||
},
|
||||
&EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
State: &instanceRefreshState,
|
||||
ProviderSchema: &providerSchema,
|
||||
targetState: refreshState,
|
||||
Dependencies: &n.Dependencies,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// Plan the instance
|
||||
&EvalDiff{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
CreateBeforeDestroy: n.ForceCreateBeforeDestroy,
|
||||
Provider: &provider,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &instanceRefreshState,
|
||||
OutputChange: &change,
|
||||
OutputState: &instancePlanState,
|
||||
},
|
||||
&EvalCheckPreventDestroy{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
Change: &change,
|
||||
},
|
||||
&EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
State: &instancePlanState,
|
||||
ProviderSchema: &providerSchema,
|
||||
},
|
||||
&EvalWriteDiff{
|
||||
Addr: addr.Resource,
|
||||
ProviderSchema: &providerSchema,
|
||||
Change: &change,
|
||||
},
|
||||
},
|
||||
provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validateSelfRef := &EvalValidateSelfRef{
|
||||
Addr: addr.Resource,
|
||||
Config: config.Config,
|
||||
ProviderSchema: &providerSchema,
|
||||
}
|
||||
_, err = validateSelfRef.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Refresh, maybe
|
||||
if !skipRefresh {
|
||||
instanceRefreshState, err = n.ReadResourceInstanceState(ctx, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
refreshLifecycle := &EvalRefreshLifecycle{
|
||||
Addr: addr,
|
||||
Config: n.Config,
|
||||
State: &instanceRefreshState,
|
||||
ForceCreateBeforeDestroy: n.ForceCreateBeforeDestroy,
|
||||
}
|
||||
_, err = refreshLifecycle.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
refresh := &EvalRefresh{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
Provider: &provider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &instanceRefreshState,
|
||||
Output: &instanceRefreshState,
|
||||
}
|
||||
_, err = refresh.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeRefreshState := &EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &instanceRefreshState,
|
||||
targetState: refreshState,
|
||||
Dependencies: &n.Dependencies,
|
||||
}
|
||||
_, err = writeRefreshState.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Plan the instance
|
||||
diff := &EvalDiff{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
CreateBeforeDestroy: n.ForceCreateBeforeDestroy,
|
||||
Provider: &provider,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &instanceRefreshState,
|
||||
OutputChange: &change,
|
||||
OutputState: &instancePlanState,
|
||||
}
|
||||
_, err = diff.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = n.checkPreventDestroy(change)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeState := &EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
State: &instancePlanState,
|
||||
ProviderSchema: &providerSchema,
|
||||
}
|
||||
_, err = writeState.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writeDiff := &EvalWriteDiff{
|
||||
Addr: addr.Resource,
|
||||
ProviderSchema: &providerSchema,
|
||||
Change: &change,
|
||||
}
|
||||
_, err = writeDiff.Eval(ctx)
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ func (n *NodePlannableResourceInstanceOrphan) Execute(ctx EvalContext, op walkOp
|
|||
return err
|
||||
}
|
||||
|
||||
err = n.CheckPreventDestroy(addr, change)
|
||||
err = n.checkPreventDestroy(change)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue