apply refactor

This commit is contained in:
Kristin Laemmert 2020-09-28 16:40:40 -04:00
parent 26260c47f0
commit 3bb64e80d5
1 changed files with 309 additions and 295 deletions

View File

@ -6,7 +6,6 @@ import (
"github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/configs"
"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"
"github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/tfdiags"
) )
@ -34,7 +33,7 @@ var (
_ GraphNodeCreator = (*NodeApplyableResourceInstance)(nil) _ GraphNodeCreator = (*NodeApplyableResourceInstance)(nil)
_ GraphNodeReferencer = (*NodeApplyableResourceInstance)(nil) _ GraphNodeReferencer = (*NodeApplyableResourceInstance)(nil)
_ GraphNodeDeposer = (*NodeApplyableResourceInstance)(nil) _ GraphNodeDeposer = (*NodeApplyableResourceInstance)(nil)
_ GraphNodeEvalable = (*NodeApplyableResourceInstance)(nil) _ GraphNodeExecutable = (*NodeApplyableResourceInstance)(nil)
_ GraphNodeAttachDependencies = (*NodeApplyableResourceInstance)(nil) _ GraphNodeAttachDependencies = (*NodeApplyableResourceInstance)(nil)
) )
@ -101,8 +100,8 @@ func (n *NodeApplyableResourceInstance) AttachDependencies(deps []addrs.ConfigRe
n.Dependencies = deps n.Dependencies = deps
} }
// GraphNodeEvalable // GraphNodeExecutable
func (n *NodeApplyableResourceInstance) EvalTree() EvalNode { func (n *NodeApplyableResourceInstance) Execute(ctx EvalContext, op walkOperation) error {
addr := n.ResourceInstanceAddr() addr := n.ResourceInstanceAddr()
if n.Config == nil { if n.Config == nil {
@ -120,61 +119,44 @@ func (n *NodeApplyableResourceInstance) EvalTree() EvalNode {
addr, addr,
), ),
)) ))
err := diags.Err() return diags.Err()
return &EvalReturnError{
Error: &err,
}
} }
// Eval info is different depending on what kind of resource this is // Eval info is different depending on what kind of resource this is
switch n.Config.Mode { switch n.Config.Mode {
case addrs.ManagedResourceMode: case addrs.ManagedResourceMode:
return n.evalTreeManagedResource(addr) return n.managedResourceExecute(ctx)
case addrs.DataResourceMode: case addrs.DataResourceMode:
return n.evalTreeDataResource(addr) return n.dataResourceExecute(ctx)
default: default:
panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode)) panic(fmt.Errorf("unsupported resource mode %s", n.Config.Mode))
} }
} }
func (n *NodeApplyableResourceInstance) evalTreeDataResource(addr addrs.AbsResourceInstance) EvalNode { func (n *NodeApplyableResourceInstance) dataResourceExecute(ctx EvalContext) error {
var provider providers.Interface addr := n.ResourceInstanceAddr().Resource
var providerSchema *ProviderSchema
var change *plans.ResourceInstanceChange
var state *states.ResourceInstanceObject
return &EvalSequence{ provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
Nodes: []EvalNode{ if err != nil {
&EvalGetProvider{ return err
Addr: n.ResolvedProvider, }
Output: &provider,
Schema: &providerSchema, change, err := n.ReadDiff(ctx, providerSchema)
}, if err != nil {
return err
// Get the saved diff for apply }
&EvalReadDiff{ // Stop early if we don't actually have a diff
Addr: addr.Resource, if change == nil {
ProviderSchema: &providerSchema, return EvalEarlyExitError{}
Change: &change,
},
// Stop early if we don't actually have a diff
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
if change == nil {
return true, EvalEarlyExitError{}
} }
return true, nil
},
Then: EvalNoop{},
},
// In this particular call to EvalReadData we include our planned // In this particular call to EvalReadData we include our planned
// change, which signals that we expect this read to complete fully // change, which signals that we expect this read to complete fully
// with no unknown values; it'll produce an error if not. // with no unknown values; it'll produce an error if not.
&evalReadDataApply{ var state *states.ResourceInstanceObject
readDataApply := &evalReadDataApply{
evalReadData{ evalReadData{
Addr: addr.Resource, Addr: addr,
Config: n.Config, Config: n.Config,
Planned: &change, Planned: &change,
Provider: &provider, Provider: &provider,
@ -183,112 +165,105 @@ func (n *NodeApplyableResourceInstance) evalTreeDataResource(addr addrs.AbsResou
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
State: &state, State: &state,
}, },
}, }
_, err = readDataApply.Eval(ctx)
if err != nil {
return err
}
&EvalWriteState{ writeState := &EvalWriteState{
Addr: addr.Resource, Addr: addr,
ProviderAddr: n.ResolvedProvider, ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
State: &state, State: &state,
}, }
_, err = writeState.Eval(ctx)
if err != nil {
return err
}
// Clear the diff now that we've applied it, so writeDiff := &EvalWriteDiff{
// later nodes won't see a diff that's now a no-op. Addr: addr,
&EvalWriteDiff{
Addr: addr.Resource,
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
Change: nil, Change: nil,
},
&EvalUpdateStateHook{},
},
} }
_, err = writeDiff.Eval(ctx)
if err != nil {
return err
} }
func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsResourceInstance) EvalNode { UpdateStateHook(ctx)
return nil
}
func (n *NodeApplyableResourceInstance) managedResourceExecute(ctx EvalContext) error {
// Declare a bunch of variables that are used for state during // Declare a bunch of variables that are used for state during
// evaluation. Most of this are written to by-address below. // evaluation. Most of this are written to by-address below.
var provider providers.Interface
var providerSchema *ProviderSchema
var diff, diffApply *plans.ResourceInstanceChange
var state *states.ResourceInstanceObject var state *states.ResourceInstanceObject
var err error
var createNew bool var createNew bool
var createBeforeDestroyEnabled bool var createBeforeDestroyEnabled bool
var deposedKey states.DeposedKey var deposedKey states.DeposedKey
return &EvalSequence{ addr := n.ResourceInstanceAddr().Resource
Nodes: []EvalNode{ provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
&EvalGetProvider{ if err != nil {
Addr: n.ResolvedProvider, return err
Output: &provider, }
Schema: &providerSchema,
},
// Get the saved diff for apply // Get the saved diff for apply
&EvalReadDiff{ diffApply, err := n.ReadDiff(ctx, providerSchema)
Addr: addr.Resource, if err != nil {
ProviderSchema: &providerSchema, return err
Change: &diffApply, }
},
// We don't want to do any destroys // We don't want to do any destroys
// (these are handled by NodeDestroyResourceInstance instead) // (these are handled by NodeDestroyResourceInstance instead)
&EvalIf{ if diffApply == nil || diffApply.Action == plans.Delete {
If: func(ctx EvalContext) (bool, error) { return EvalEarlyExitError{}
if diffApply == nil {
return true, EvalEarlyExitError{}
} }
if diffApply.Action == plans.Delete {
return true, EvalEarlyExitError{}
}
return true, nil
},
Then: EvalNoop{},
},
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
destroy := false
if diffApply != nil {
destroy = (diffApply.Action == plans.Delete || diffApply.Action.IsReplace())
destroy := (diffApply.Action == plans.Delete || diffApply.Action.IsReplace())
// Get the stored action for CBD if we have a plan already // Get the stored action for CBD if we have a plan already
createBeforeDestroyEnabled = diffApply.Change.Action == plans.CreateThenDelete createBeforeDestroyEnabled = diffApply.Change.Action == plans.CreateThenDelete
}
if destroy && n.CreateBeforeDestroy() { if destroy && n.CreateBeforeDestroy() {
createBeforeDestroyEnabled = true createBeforeDestroyEnabled = true
} }
return createBeforeDestroyEnabled, nil if createBeforeDestroyEnabled {
}, deposeState := &EvalDeposeState{
Then: &EvalDeposeState{ Addr: addr,
Addr: addr.Resource,
ForceKey: n.PreallocatedDeposedKey, ForceKey: n.PreallocatedDeposedKey,
OutputKey: &deposedKey, OutputKey: &deposedKey,
}, }
}, _, err = deposeState.Eval(ctx)
if err != nil {
return err
}
}
&EvalReadState{ readState := &EvalReadState{
Addr: addr.Resource, Addr: addr,
Provider: &provider, Provider: &provider,
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
Output: &state, Output: &state,
}, }
_, err = readState.Eval(ctx)
if err != nil {
return err
}
// Get the saved diff // Get the saved diff
&EvalReadDiff{ diff, err := n.ReadDiff(ctx, providerSchema)
Addr: addr.Resource, if err != nil {
ProviderSchema: &providerSchema, return err
Change: &diff, }
},
// Make a new diff, in case we've learned new values in the state // Make a new diff, in case we've learned new values in the state
// during apply which we can now incorporate. // during apply which we can now incorporate.
&EvalDiff{ evalDiff := &EvalDiff{
Addr: addr.Resource, Addr: addr,
Config: n.Config, Config: n.Config,
Provider: &provider, Provider: &provider,
ProviderAddr: n.ResolvedProvider, ProviderAddr: n.ResolvedProvider,
@ -298,58 +273,68 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
PreviousDiff: &diff, PreviousDiff: &diff,
OutputChange: &diffApply, OutputChange: &diffApply,
OutputState: &state, OutputState: &state,
}, }
_, err = evalDiff.Eval(ctx)
if err != nil {
return err
}
// Compare the diffs // Compare the diffs
&EvalCheckPlannedChange{ checkPlannedChange := &EvalCheckPlannedChange{
Addr: addr.Resource, Addr: addr,
ProviderAddr: n.ResolvedProvider, ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
Planned: &diff, Planned: &diff,
Actual: &diffApply, Actual: &diffApply,
}, }
_, err = checkPlannedChange.Eval(ctx)
if err != nil {
return err
}
&EvalGetProvider{ readState = &EvalReadState{
Addr: n.ResolvedProvider, Addr: addr,
Output: &provider,
Schema: &providerSchema,
},
&EvalReadState{
Addr: addr.Resource,
Provider: &provider, Provider: &provider,
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
Output: &state, Output: &state,
}, }
_, err = readState.Eval(ctx)
if err != nil {
return err
}
&EvalReduceDiff{ reduceDiff := &EvalReduceDiff{
Addr: addr.Resource, Addr: addr,
InChange: &diffApply, InChange: &diffApply,
Destroy: false, Destroy: false,
OutChange: &diffApply, OutChange: &diffApply,
}, }
_, err = reduceDiff.Eval(ctx)
if err != nil {
return err
}
// EvalReduceDiff may have simplified our planned change // EvalReduceDiff may have simplified our planned change
// into a NoOp if it only requires destroying, since destroying // into a NoOp if it only requires destroying, since destroying
// is handled by NodeDestroyResourceInstance. // is handled by NodeDestroyResourceInstance.
&EvalIf{
If: func(ctx EvalContext) (bool, error) {
if diffApply == nil || diffApply.Action == plans.NoOp { if diffApply == nil || diffApply.Action == plans.NoOp {
return true, EvalEarlyExitError{} return EvalEarlyExitError{}
} }
return true, nil
},
Then: EvalNoop{},
},
// Call pre-apply hook evalApplyPre := &EvalApplyPre{
&EvalApplyPre{ Addr: addr,
Addr: addr.Resource,
State: &state, State: &state,
Change: &diffApply, Change: &diffApply,
}, }
&EvalApply{ _, err = evalApplyPre.Eval(ctx)
Addr: addr.Resource, if err != nil {
return err
}
var applyError error
evalApply := &EvalApply{
Addr: addr,
Config: n.Config, Config: n.Config,
State: &state, State: &state,
Change: &diffApply, Change: &diffApply,
@ -358,81 +343,110 @@ func (n *NodeApplyableResourceInstance) evalTreeManagedResource(addr addrs.AbsRe
ProviderMetas: n.ProviderMetas, ProviderMetas: n.ProviderMetas,
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
Output: &state, Output: &state,
Error: &err, Error: &applyError,
CreateNew: &createNew, CreateNew: &createNew,
CreateBeforeDestroy: n.CreateBeforeDestroy(), CreateBeforeDestroy: n.CreateBeforeDestroy(),
}, }
&EvalMaybeTainted{ _, err = evalApply.Eval(ctx)
Addr: addr.Resource, if err != nil {
return err
}
evalMaybeTainted := &EvalMaybeTainted{
Addr: addr,
State: &state, State: &state,
Change: &diffApply, Change: &diffApply,
Error: &err, Error: &applyError,
}, }
&EvalWriteState{ _, err = evalMaybeTainted.Eval(ctx)
Addr: addr.Resource, if err != nil {
return err
}
writeState := &EvalWriteState{
Addr: addr,
ProviderAddr: n.ResolvedProvider, ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
State: &state, State: &state,
Dependencies: &n.Dependencies, Dependencies: &n.Dependencies,
}, }
&EvalApplyProvisioners{ _, err = writeState.Eval(ctx)
Addr: addr.Resource, if err != nil {
return err
}
applyProvisioners := &EvalApplyProvisioners{
Addr: addr,
State: &state, // EvalApplyProvisioners will skip if already tainted State: &state, // EvalApplyProvisioners will skip if already tainted
ResourceConfig: n.Config, ResourceConfig: n.Config,
CreateNew: &createNew, CreateNew: &createNew,
Error: &err, Error: &applyError,
When: configs.ProvisionerWhenCreate, When: configs.ProvisionerWhenCreate,
}, }
&EvalMaybeTainted{ _, err = applyProvisioners.Eval(ctx)
Addr: addr.Resource, if err != nil {
return err
}
evalMaybeTainted = &EvalMaybeTainted{
Addr: addr,
State: &state, State: &state,
Change: &diffApply, Change: &diffApply,
Error: &err, Error: &applyError,
}, }
&EvalWriteState{ _, err = evalMaybeTainted.Eval(ctx)
Addr: addr.Resource, if err != nil {
return err
}
writeState = &EvalWriteState{
Addr: addr,
ProviderAddr: n.ResolvedProvider, ProviderAddr: n.ResolvedProvider,
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
State: &state, State: &state,
Dependencies: &n.Dependencies, Dependencies: &n.Dependencies,
}, }
&EvalIf{ _, err = writeState.Eval(ctx)
If: func(ctx EvalContext) (bool, error) { if err != nil {
return createBeforeDestroyEnabled && err != nil, nil return err
}, }
Then: &EvalMaybeRestoreDeposedObject{
Addr: addr.Resource, if createBeforeDestroyEnabled && applyError != nil {
maybeRestoreDesposedObject := &EvalMaybeRestoreDeposedObject{
Addr: addr,
PlannedChange: &diffApply, PlannedChange: &diffApply,
Key: &deposedKey, Key: &deposedKey,
}, }
}, _, err := maybeRestoreDesposedObject.Eval(ctx)
if err != nil {
return err
}
}
// We clear the diff out here so that future nodes // We clear the diff out here so that future nodes don't see a diff that is
// don't see a diff that is already complete. There // already complete. There is no longer a diff!
// is no longer a diff! if !diff.Action.IsReplace() || !n.CreateBeforeDestroy() {
&EvalIf{ writeDiff := &EvalWriteDiff{
If: func(ctx EvalContext) (bool, error) { Addr: addr,
if !diff.Action.IsReplace() {
return true, nil
}
if !n.CreateBeforeDestroy() {
return true, nil
}
return false, nil
},
Then: &EvalWriteDiff{
Addr: addr.Resource,
ProviderSchema: &providerSchema, ProviderSchema: &providerSchema,
Change: nil, Change: nil,
}, }
}, _, err := writeDiff.Eval(ctx)
if err != nil {
return err
}
}
&EvalApplyPost{ applyPost := &EvalApplyPost{
Addr: addr.Resource, Addr: addr,
State: &state, State: &state,
Error: &err, Error: &applyError,
},
&EvalUpdateStateHook{},
},
} }
_, err = applyPost.Eval(ctx)
if err != nil {
return err
}
UpdateStateHook(ctx)
return nil
} }