terraform: refactor Node*Ouput
This commit refactors NodeApplyableOutput and NodeDestroyableOutput into the new Execute() pattern, collapsing the functions in eval_output.go into one place. I also reverted a recent decision to have Execute take a _pointer_ to a walkOperation: I was thinking of interfaces, not constant bytes, so all it did was cause problems. And finally I removed eval_lang.go, which was unused.
This commit is contained in:
parent
e191a57093
commit
069f379e75
|
@ -1,61 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
|
||||||
|
|
||||||
"github.com/hashicorp/hcl/v2"
|
|
||||||
"github.com/hashicorp/terraform/configs/configschema"
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EvalConfigBlock is an EvalNode implementation that takes a raw
|
|
||||||
// configuration block and evaluates any expressions within it.
|
|
||||||
//
|
|
||||||
// ExpandedConfig is populated with the result of expanding any "dynamic"
|
|
||||||
// blocks in the given body, which can be useful for extracting correct source
|
|
||||||
// location information for specific attributes in the result.
|
|
||||||
type EvalConfigBlock struct {
|
|
||||||
Config *hcl.Body
|
|
||||||
Schema *configschema.Block
|
|
||||||
SelfAddr addrs.Referenceable
|
|
||||||
Output *cty.Value
|
|
||||||
ExpandedConfig *hcl.Body
|
|
||||||
ContinueOnErr bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *EvalConfigBlock) Eval(ctx EvalContext) (interface{}, error) {
|
|
||||||
val, body, diags := ctx.EvaluateBlock(*n.Config, n.Schema, n.SelfAddr, EvalDataForNoInstanceKey)
|
|
||||||
if diags.HasErrors() && n.ContinueOnErr {
|
|
||||||
log.Printf("[WARN] Block evaluation failed: %s", diags.Err())
|
|
||||||
return nil, EvalEarlyExitError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.Output != nil {
|
|
||||||
*n.Output = val
|
|
||||||
}
|
|
||||||
if n.ExpandedConfig != nil {
|
|
||||||
*n.ExpandedConfig = body
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, diags.ErrWithWarnings()
|
|
||||||
}
|
|
||||||
|
|
||||||
// EvalConfigExpr is an EvalNode implementation that takes a raw configuration
|
|
||||||
// expression and evaluates it.
|
|
||||||
type EvalConfigExpr struct {
|
|
||||||
Expr hcl.Expression
|
|
||||||
SelfAddr addrs.Referenceable
|
|
||||||
Output *cty.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *EvalConfigExpr) Eval(ctx EvalContext) (interface{}, error) {
|
|
||||||
val, diags := ctx.EvaluateExpr(n.Expr, cty.DynamicPseudoType, n.SelfAddr)
|
|
||||||
|
|
||||||
if n.Output != nil {
|
|
||||||
*n.Output = val
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, diags.ErrWithWarnings()
|
|
||||||
}
|
|
|
@ -1,138 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
|
||||||
"github.com/hashicorp/terraform/configs"
|
|
||||||
"github.com/hashicorp/terraform/plans"
|
|
||||||
"github.com/hashicorp/terraform/states"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EvalDeleteOutput is an EvalNode implementation that deletes an output
|
|
||||||
// from the state.
|
|
||||||
type EvalDeleteOutput struct {
|
|
||||||
Addr addrs.AbsOutputValue
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: test
|
|
||||||
func (n *EvalDeleteOutput) Eval(ctx EvalContext) (interface{}, error) {
|
|
||||||
state := ctx.State()
|
|
||||||
if state == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
state.RemoveOutputValue(n.Addr)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EvalWriteOutput is an EvalNode implementation that writes the output
|
|
||||||
// for the given name to the current state.
|
|
||||||
type EvalWriteOutput struct {
|
|
||||||
Addr addrs.OutputValue
|
|
||||||
Config *configs.Output
|
|
||||||
// ContinueOnErr allows interpolation to fail during Input
|
|
||||||
ContinueOnErr bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: test
|
|
||||||
func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) {
|
|
||||||
addr := n.Addr.Absolute(ctx.Path())
|
|
||||||
|
|
||||||
// This has to run before we have a state lock, since evaluation also
|
|
||||||
// reads the state
|
|
||||||
val, diags := ctx.EvaluateExpr(n.Config.Expr, cty.DynamicPseudoType, nil)
|
|
||||||
// We'll handle errors below, after we have loaded the module.
|
|
||||||
|
|
||||||
// Outputs don't have a separate mode for validation, so validate
|
|
||||||
// depends_on expressions here too
|
|
||||||
diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn))
|
|
||||||
|
|
||||||
state := ctx.State()
|
|
||||||
if state == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
changes := ctx.Changes() // may be nil, if we're not working on a changeset
|
|
||||||
|
|
||||||
// handling the interpolation error
|
|
||||||
if diags.HasErrors() {
|
|
||||||
if n.ContinueOnErr || flagWarnOutputErrors {
|
|
||||||
log.Printf("[ERROR] Output interpolation %q failed: %s", n.Addr.Name, diags.Err())
|
|
||||||
// if we're continuing, make sure the output is included, and
|
|
||||||
// marked as unknown. If the evaluator was able to find a type
|
|
||||||
// for the value in spite of the error then we'll use it.
|
|
||||||
n.setValue(addr, state, changes, cty.UnknownVal(val.Type()))
|
|
||||||
return nil, EvalEarlyExitError{}
|
|
||||||
}
|
|
||||||
return nil, diags.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
n.setValue(addr, state, changes, val)
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *EvalWriteOutput) setValue(addr addrs.AbsOutputValue, state *states.SyncState, changes *plans.ChangesSync, val cty.Value) {
|
|
||||||
if val.IsKnown() && !val.IsNull() {
|
|
||||||
// The state itself doesn't represent unknown values, so we null them
|
|
||||||
// out here and then we'll save the real unknown value in the planned
|
|
||||||
// changeset below, if we have one on this graph walk.
|
|
||||||
log.Printf("[TRACE] EvalWriteOutput: Saving value for %s in state", addr)
|
|
||||||
stateVal := cty.UnknownAsNull(val)
|
|
||||||
state.SetOutputValue(addr, stateVal, n.Config.Sensitive)
|
|
||||||
} else {
|
|
||||||
log.Printf("[TRACE] EvalWriteOutput: Removing %s from state (it is now null)", addr)
|
|
||||||
state.RemoveOutputValue(addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we also have an active changeset then we'll replicate the value in
|
|
||||||
// there. This is used in preference to the state where present, since it
|
|
||||||
// *is* able to represent unknowns, while the state cannot.
|
|
||||||
if changes != nil {
|
|
||||||
// For the moment we are not properly tracking changes to output
|
|
||||||
// values, and just marking them always as "Create" or "Destroy"
|
|
||||||
// actions. A future release will rework the output lifecycle so we
|
|
||||||
// can track their changes properly, in a similar way to how we work
|
|
||||||
// with resource instances.
|
|
||||||
|
|
||||||
var change *plans.OutputChange
|
|
||||||
if !val.IsNull() {
|
|
||||||
change = &plans.OutputChange{
|
|
||||||
Addr: addr,
|
|
||||||
Sensitive: n.Config.Sensitive,
|
|
||||||
Change: plans.Change{
|
|
||||||
Action: plans.Create,
|
|
||||||
Before: cty.NullVal(cty.DynamicPseudoType),
|
|
||||||
After: val,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
change = &plans.OutputChange{
|
|
||||||
Addr: addr,
|
|
||||||
Sensitive: n.Config.Sensitive,
|
|
||||||
Change: plans.Change{
|
|
||||||
// This is just a weird placeholder delete action since
|
|
||||||
// we don't have an actual prior value to indicate.
|
|
||||||
// FIXME: Generate real planned changes for output values
|
|
||||||
// that include the old values.
|
|
||||||
Action: plans.Delete,
|
|
||||||
Before: cty.NullVal(cty.DynamicPseudoType),
|
|
||||||
After: cty.NullVal(cty.DynamicPseudoType),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cs, err := change.Encode()
|
|
||||||
if err != nil {
|
|
||||||
// Should never happen, since we just constructed this right above
|
|
||||||
panic(fmt.Sprintf("planned change for %s could not be encoded: %s", addr, err))
|
|
||||||
}
|
|
||||||
log.Printf("[TRACE] EvalWriteOutput: Saving %s change for %s in changeset", change.Action, addr)
|
|
||||||
changes.RemoveOutputChange(addr) // remove any existing planned change, if present
|
|
||||||
changes.AppendOutputChange(cs) // add the new planned change
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,5 +5,5 @@ package terraform
|
||||||
// the process of being removed. A given graph node should _not_ implement both
|
// the process of being removed. A given graph node should _not_ implement both
|
||||||
// GraphNodeExecutable and GraphNodeEvalable.
|
// GraphNodeExecutable and GraphNodeEvalable.
|
||||||
type GraphNodeExecutable interface {
|
type GraphNodeExecutable interface {
|
||||||
Execute(EvalContext, *walkOperation) error
|
Execute(EvalContext, walkOperation) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ func (w *ContextGraphWalker) Execute(ctx EvalContext, n GraphNodeExecutable) tfd
|
||||||
// Acquire a lock on the semaphore
|
// Acquire a lock on the semaphore
|
||||||
w.Context.parallelSem.Acquire()
|
w.Context.parallelSem.Acquire()
|
||||||
|
|
||||||
err := n.Execute(ctx, &w.Operation)
|
err := n.Execute(ctx, w.Operation)
|
||||||
|
|
||||||
// Release the semaphore
|
// Release the semaphore
|
||||||
w.Context.parallelSem.Release()
|
w.Context.parallelSem.Release()
|
||||||
|
|
|
@ -13,7 +13,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GraphNodeExecutable
|
// GraphNodeExecutable
|
||||||
func (n *NodeDestroyableDataResourceInstance) Execute(ctx EvalContext, op *walkOperation) error {
|
func (n *NodeDestroyableDataResourceInstance) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
log.Printf("[TRACE] NodeDestroyableDataResourceInstance: removing state object for %s", n.Addr)
|
log.Printf("[TRACE] NodeDestroyableDataResourceInstance: removing state object for %s", n.Addr)
|
||||||
ctx.State().SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider)
|
ctx.State().SetResourceInstanceCurrent(n.Addr, nil, n.ResolvedProvider)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestNodeDataDestroyExecute(t *testing.T) {
|
||||||
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
}}
|
}}
|
||||||
|
|
||||||
err := node.Execute(ctx, nil)
|
err := node.Execute(ctx, walkApply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %s", err.Error())
|
t.Fatalf("unexpected error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -191,7 +191,7 @@ type NodeRefreshableDataResourceInstance struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeExecutable
|
// GraphNodeExecutable
|
||||||
func (n *NodeRefreshableDataResourceInstance) Execute(ctx EvalContext, op *walkOperation) error {
|
func (n *NodeRefreshableDataResourceInstance) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
addr := n.ResourceInstanceAddr()
|
addr := n.ResourceInstanceAddr()
|
||||||
|
|
||||||
// These variables are the state for the eval sequence below, and are
|
// These variables are the state for the eval sequence below, and are
|
||||||
|
@ -280,7 +280,5 @@ func (n *NodeRefreshableDataResourceInstance) Execute(ctx EvalContext, op *walkO
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@ import (
|
||||||
"github.com/hashicorp/terraform/configs"
|
"github.com/hashicorp/terraform/configs"
|
||||||
"github.com/hashicorp/terraform/dag"
|
"github.com/hashicorp/terraform/dag"
|
||||||
"github.com/hashicorp/terraform/lang"
|
"github.com/hashicorp/terraform/lang"
|
||||||
|
"github.com/hashicorp/terraform/plans"
|
||||||
|
"github.com/hashicorp/terraform/states"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
// nodeExpandOutput is the placeholder for an output that has not yet had
|
// nodeExpandOutput is the placeholder for an output that has not yet had
|
||||||
|
@ -111,7 +114,7 @@ var (
|
||||||
_ GraphNodeReferenceable = (*NodeApplyableOutput)(nil)
|
_ GraphNodeReferenceable = (*NodeApplyableOutput)(nil)
|
||||||
_ GraphNodeReferencer = (*NodeApplyableOutput)(nil)
|
_ GraphNodeReferencer = (*NodeApplyableOutput)(nil)
|
||||||
_ GraphNodeReferenceOutside = (*NodeApplyableOutput)(nil)
|
_ GraphNodeReferenceOutside = (*NodeApplyableOutput)(nil)
|
||||||
_ GraphNodeEvalable = (*NodeApplyableOutput)(nil)
|
_ GraphNodeExecutable = (*NodeApplyableOutput)(nil)
|
||||||
_ graphNodeTemporaryValue = (*NodeApplyableOutput)(nil)
|
_ graphNodeTemporaryValue = (*NodeApplyableOutput)(nil)
|
||||||
_ dag.GraphNodeDotter = (*NodeApplyableOutput)(nil)
|
_ dag.GraphNodeDotter = (*NodeApplyableOutput)(nil)
|
||||||
)
|
)
|
||||||
|
@ -193,18 +196,43 @@ func (n *NodeApplyableOutput) References() []*addrs.Reference {
|
||||||
return referencesForOutput(n.Config)
|
return referencesForOutput(n.Config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable
|
// GraphNodeExecutable
|
||||||
func (n *NodeApplyableOutput) EvalTree() EvalNode {
|
func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
return &EvalSequence{
|
switch op {
|
||||||
Nodes: []EvalNode{
|
// Everything except walkImport
|
||||||
&EvalOpFilter{
|
case walkEval, walkRefresh, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy:
|
||||||
Ops: []walkOperation{walkEval, walkRefresh, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy},
|
// This has to run before we have a state lock, since evaluation also
|
||||||
Node: &EvalWriteOutput{
|
// reads the state
|
||||||
Addr: n.Addr.OutputValue,
|
val, diags := ctx.EvaluateExpr(n.Config.Expr, cty.DynamicPseudoType, nil)
|
||||||
Config: n.Config,
|
// We'll handle errors below, after we have loaded the module.
|
||||||
},
|
|
||||||
},
|
// Outputs don't have a separate mode for validation, so validate
|
||||||
},
|
// depends_on expressions here too
|
||||||
|
diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn))
|
||||||
|
|
||||||
|
state := ctx.State()
|
||||||
|
if state == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
changes := ctx.Changes() // may be nil, if we're not working on a changeset
|
||||||
|
|
||||||
|
// handling the interpolation error
|
||||||
|
if diags.HasErrors() {
|
||||||
|
if flagWarnOutputErrors {
|
||||||
|
log.Printf("[ERROR] Output interpolation %q failed: %s", n.Addr, diags.Err())
|
||||||
|
// if we're continuing, make sure the output is included, and
|
||||||
|
// marked as unknown. If the evaluator was able to find a type
|
||||||
|
// for the value in spite of the error then we'll use it.
|
||||||
|
n.setValue(state, changes, cty.UnknownVal(val.Type()))
|
||||||
|
return EvalEarlyExitError{}
|
||||||
|
}
|
||||||
|
return diags.Err()
|
||||||
|
}
|
||||||
|
n.setValue(state, changes, val)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +255,7 @@ type NodeDestroyableOutput struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ GraphNodeEvalable = (*NodeDestroyableOutput)(nil)
|
_ GraphNodeExecutable = (*NodeDestroyableOutput)(nil)
|
||||||
_ dag.GraphNodeDotter = (*NodeDestroyableOutput)(nil)
|
_ dag.GraphNodeDotter = (*NodeDestroyableOutput)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -245,11 +273,14 @@ func (n *NodeDestroyableOutput) temporaryValue() bool {
|
||||||
return !n.Addr.Module.IsRoot()
|
return !n.Addr.Module.IsRoot()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable
|
// GraphNodeExecutable
|
||||||
func (n *NodeDestroyableOutput) EvalTree() EvalNode {
|
func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
return &EvalDeleteOutput{
|
state := ctx.State()
|
||||||
Addr: n.Addr,
|
if state == nil {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
state.RemoveOutputValue(n.Addr)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// dag.GraphNodeDotter impl.
|
// dag.GraphNodeDotter impl.
|
||||||
|
@ -262,3 +293,64 @@ func (n *NodeDestroyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.Dot
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.ChangesSync, val cty.Value) {
|
||||||
|
if val.IsKnown() && !val.IsNull() {
|
||||||
|
// The state itself doesn't represent unknown values, so we null them
|
||||||
|
// out here and then we'll save the real unknown value in the planned
|
||||||
|
// changeset below, if we have one on this graph walk.
|
||||||
|
log.Printf("[TRACE] EvalWriteOutput: Saving value for %s in state", n.Addr)
|
||||||
|
stateVal := cty.UnknownAsNull(val)
|
||||||
|
state.SetOutputValue(n.Addr, stateVal, n.Config.Sensitive)
|
||||||
|
} else {
|
||||||
|
log.Printf("[TRACE] EvalWriteOutput: Removing %s from state (it is now null)", n.Addr)
|
||||||
|
state.RemoveOutputValue(n.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we also have an active changeset then we'll replicate the value in
|
||||||
|
// there. This is used in preference to the state where present, since it
|
||||||
|
// *is* able to represent unknowns, while the state cannot.
|
||||||
|
if changes != nil {
|
||||||
|
// For the moment we are not properly tracking changes to output
|
||||||
|
// values, and just marking them always as "Create" or "Destroy"
|
||||||
|
// actions. A future release will rework the output lifecycle so we
|
||||||
|
// can track their changes properly, in a similar way to how we work
|
||||||
|
// with resource instances.
|
||||||
|
|
||||||
|
var change *plans.OutputChange
|
||||||
|
if !val.IsNull() {
|
||||||
|
change = &plans.OutputChange{
|
||||||
|
Addr: n.Addr,
|
||||||
|
Sensitive: n.Config.Sensitive,
|
||||||
|
Change: plans.Change{
|
||||||
|
Action: plans.Create,
|
||||||
|
Before: cty.NullVal(cty.DynamicPseudoType),
|
||||||
|
After: val,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
change = &plans.OutputChange{
|
||||||
|
Addr: n.Addr,
|
||||||
|
Sensitive: n.Config.Sensitive,
|
||||||
|
Change: plans.Change{
|
||||||
|
// This is just a weird placeholder delete action since
|
||||||
|
// we don't have an actual prior value to indicate.
|
||||||
|
// FIXME: Generate real planned changes for output values
|
||||||
|
// that include the old values.
|
||||||
|
Action: plans.Delete,
|
||||||
|
Before: cty.NullVal(cty.DynamicPseudoType),
|
||||||
|
After: cty.NullVal(cty.DynamicPseudoType),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cs, err := change.Encode()
|
||||||
|
if err != nil {
|
||||||
|
// Should never happen, since we just constructed this right above
|
||||||
|
panic(fmt.Sprintf("planned change for %s could not be encoded: %s", n.Addr, err))
|
||||||
|
}
|
||||||
|
log.Printf("[TRACE] ExecuteWriteOutput: Saving %s change for %s in changeset", change.Action, n.Addr)
|
||||||
|
changes.RemoveOutputChange(n.Addr) // remove any existing planned change, if present
|
||||||
|
changes.AppendOutputChange(cs) // add the new planned change
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,14 +3,13 @@ package terraform
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/configs"
|
"github.com/hashicorp/terraform/configs"
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEvalWriteMapOutput(t *testing.T) {
|
func TestNodeApplyableOutputExecute(t *testing.T) {
|
||||||
ctx := new(MockEvalContext)
|
ctx := new(MockEvalContext)
|
||||||
ctx.StateState = states.NewState().SyncWrapper()
|
ctx.StateState = states.NewState().SyncWrapper()
|
||||||
|
|
||||||
|
@ -44,16 +43,38 @@ func TestEvalWriteMapOutput(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
evalNode := &EvalWriteOutput{
|
node := &NodeApplyableOutput{
|
||||||
Config: &configs.Output{},
|
Config: &configs.Output{},
|
||||||
Addr: addrs.OutputValue{Name: tc.name},
|
Addr: addrs.OutputValue{Name: tc.name}.Absolute(addrs.RootModuleInstance),
|
||||||
}
|
}
|
||||||
ctx.EvaluateExprResult = tc.val
|
ctx.EvaluateExprResult = tc.val
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
_, err := evalNode.Eval(ctx)
|
err := node.Execute(ctx, walkApply)
|
||||||
if err != nil && !tc.err {
|
if err != nil && !tc.err {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeDestroyableOutputExecute(t *testing.T) {
|
||||||
|
outputAddr := addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance)
|
||||||
|
|
||||||
|
state := states.NewState()
|
||||||
|
state.Module(addrs.RootModuleInstance).SetOutputValue("foo", cty.StringVal("bar"), false)
|
||||||
|
state.OutputValue(outputAddr)
|
||||||
|
|
||||||
|
ctx := &MockEvalContext{
|
||||||
|
StateState: state.SyncWrapper(),
|
||||||
|
}
|
||||||
|
node := NodeDestroyableOutput{Addr: outputAddr}
|
||||||
|
|
||||||
|
err := node.Execute(ctx, walkApply)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %s", err.Error())
|
||||||
|
}
|
||||||
|
if state.OutputValue(outputAddr) != nil {
|
||||||
|
t.Fatal("Unexpected outputs in state after removal")
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -209,7 +209,7 @@ func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *addrs.AbsResourc
|
||||||
}
|
}
|
||||||
|
|
||||||
// GraphNodeEvalable
|
// GraphNodeEvalable
|
||||||
func (n *NodeRefreshableManagedResourceInstance) Execute(ctx EvalContext, op *walkOperation) error {
|
func (n *NodeRefreshableManagedResourceInstance) Execute(ctx EvalContext, op walkOperation) error {
|
||||||
addr := n.ResourceInstanceAddr()
|
addr := n.ResourceInstanceAddr()
|
||||||
|
|
||||||
// Eval info is different depending on what kind of resource this is
|
// Eval info is different depending on what kind of resource this is
|
||||||
|
@ -238,7 +238,7 @@ func (n *NodeRefreshableManagedResourceInstance) Execute(ctx EvalContext, op *wa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dn.Execute(ctx, nil)
|
return dn.Execute(ctx, op)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("unsupported resource mode %s", addr.Resource.Resource.Mode))
|
panic(fmt.Errorf("unsupported resource mode %s", addr.Resource.Resource.Mode))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue