mirror of https://github.com/hashicorp/terraform
parent
184893d1e4
commit
0ac53ae3ed
@ -1,62 +0,0 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
// EvalNode is the interface that must be implemented by graph nodes to
|
||||
// evaluate/execute.
|
||||
type EvalNode interface {
|
||||
// Eval evaluates this node with the given context. The second parameter
|
||||
// are the argument values. These will match in order and 1-1 with the
|
||||
// results of the Args() return value.
|
||||
Eval(EvalContext) (interface{}, error)
|
||||
}
|
||||
|
||||
// GraphNodeEvalable is the interface that graph nodes must implement
|
||||
// to enable valuation.
|
||||
type GraphNodeEvalable interface {
|
||||
EvalTree() EvalNode
|
||||
}
|
||||
|
||||
// EvalEarlyExitError is a special error return value that can be returned
|
||||
// by eval nodes that does an early exit.
|
||||
type EvalEarlyExitError struct{}
|
||||
|
||||
func (EvalEarlyExitError) Error() string { return "early exit" }
|
||||
|
||||
// Eval evaluates the given EvalNode with the given context, properly
|
||||
// evaluating all args in the correct order.
|
||||
func Eval(n EvalNode, ctx EvalContext) (interface{}, error) {
|
||||
// Call the lower level eval which doesn't understand early exit,
|
||||
// and if we early exit, it isn't an error.
|
||||
result, err := EvalRaw(n, ctx)
|
||||
if err != nil {
|
||||
if _, ok := err.(EvalEarlyExitError); ok {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
// EvalRaw is like Eval except that it returns all errors, even if they
|
||||
// signal something normal such as EvalEarlyExitError.
|
||||
func EvalRaw(n EvalNode, ctx EvalContext) (interface{}, error) {
|
||||
log.Printf("[TRACE] eval: %T", n)
|
||||
output, err := n.Eval(ctx)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case EvalEarlyExitError:
|
||||
log.Printf("[TRACE] eval: %T, early exit err: %s", n, err)
|
||||
case tfdiags.NonFatalError:
|
||||
log.Printf("[WARN] eval: %T, non-fatal err: %s", n, err)
|
||||
default:
|
||||
log.Printf("[ERROR] eval: %T, err: %s", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
return output, err
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/terraform/plans"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
// EvalPreventDestroy is an EvalNode implementation that returns an
|
||||
// error if a resource has PreventDestroy configured and the diff
|
||||
// would destroy the resource.
|
||||
type EvalCheckPreventDestroy struct {
|
||||
Addr addrs.ResourceInstance
|
||||
Config *configs.Resource
|
||||
Change **plans.ResourceInstanceChange
|
||||
}
|
||||
|
||||
func (n *EvalCheckPreventDestroy) Eval(ctx EvalContext) (interface{}, error) {
|
||||
if n.Change == nil || *n.Change == nil || n.Config == nil || n.Config.Managed == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
change := *n.Change
|
||||
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.Absolute(ctx.Path()).String(),
|
||||
),
|
||||
Subject: &n.Config.DeclRange,
|
||||
})
|
||||
return nil, diags.Err()
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@ -1,20 +1,7 @@
|
||||
package terraform
|
||||
|
||||
// EvalReturnError is an EvalNode implementation that returns an
|
||||
// error if it is present.
|
||||
//
|
||||
// This is useful for scenarios where an error has been captured by
|
||||
// another EvalNode (like EvalApply) for special EvalTree-based error
|
||||
// handling, and that handling has completed, so the error should be
|
||||
// returned normally.
|
||||
type EvalReturnError struct {
|
||||
Error *error
|
||||
}
|
||||
// EvalEarlyExitError is a special error return value that can be returned
|
||||
// by eval nodes that does an early exit.
|
||||
type EvalEarlyExitError struct{}
|
||||
|
||||
func (n *EvalReturnError) Eval(ctx EvalContext) (interface{}, error) {
|
||||
if n.Error == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, *n.Error
|
||||
}
|
||||
func (EvalEarlyExitError) Error() string { return "early exit" }
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
package terraform
|
||||
|
||||
// EvalNodeFilterFunc is the callback used to replace a node with
|
||||
// another to node. To not do the replacement, just return the input node.
|
||||
type EvalNodeFilterFunc func(EvalNode) EvalNode
|
||||
|
||||
// EvalNodeFilterable is an interface that can be implemented by
|
||||
// EvalNodes to allow filtering of sub-elements. Note that this isn't
|
||||
// a common thing to implement and you probably don't need it.
|
||||
type EvalNodeFilterable interface {
|
||||
EvalNode
|
||||
Filter(EvalNodeFilterFunc)
|
||||
}
|
||||
|
||||
// EvalFilter runs the filter on the given node and returns the
|
||||
// final filtered value. This should be called rather than checking
|
||||
// the EvalNode directly since this will properly handle EvalNodeFilterables.
|
||||
func EvalFilter(node EvalNode, fn EvalNodeFilterFunc) EvalNode {
|
||||
if f, ok := node.(EvalNodeFilterable); ok {
|
||||
f.Filter(fn)
|
||||
return node
|
||||
}
|
||||
|
||||
return fn(node)
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
package terraform
|
||||
|
||||
// EvalNodeOpFilterable is an interface that EvalNodes can implement
|
||||
// to be filterable by the operation that is being run on Terraform.
|
||||
type EvalNodeOpFilterable interface {
|
||||
IncludeInOp(walkOperation) bool
|
||||
}
|
||||
|
||||
// EvalNodeFilterOp returns a filter function that filters nodes that
|
||||
// include themselves in specific operations.
|
||||
func EvalNodeFilterOp(op walkOperation) EvalNodeFilterFunc {
|
||||
return func(n EvalNode) EvalNode {
|
||||
include := true
|
||||
if of, ok := n.(EvalNodeOpFilterable); ok {
|
||||
include = of.IncludeInOp(op)
|
||||
}
|
||||
if include {
|
||||
return n
|
||||
}
|
||||
|
||||
return EvalNoop{}
|
||||
}
|
||||
}
|
||||
|
||||
// EvalOpFilter is an EvalNode implementation that is a proxy to
|
||||
// another node but filters based on the operation.
|
||||
type EvalOpFilter struct {
|
||||
// Ops is the list of operations to include this node in.
|
||||
Ops []walkOperation
|
||||
|
||||
// Node is the node to execute
|
||||
Node EvalNode
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalOpFilter) Eval(ctx EvalContext) (interface{}, error) {
|
||||
return EvalRaw(n.Node, ctx)
|
||||
}
|
||||
|
||||
// EvalNodeOpFilterable impl.
|
||||
func (n *EvalOpFilter) IncludeInOp(op walkOperation) bool {
|
||||
for _, v := range n.Ops {
|
||||
if v == op {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package terraform
|
||||
|
||||
// EvalIf is an EvalNode that is a conditional.
|
||||
type EvalIf struct {
|
||||
If func(EvalContext) (bool, error)
|
||||
Then EvalNode
|
||||
Else EvalNode
|
||||
}
|
||||
|
||||
// TODO: test
|
||||
func (n *EvalIf) Eval(ctx EvalContext) (interface{}, error) {
|
||||
yes, err := n.If(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if yes {
|
||||
return EvalRaw(n.Then, ctx)
|
||||
} else {
|
||||
if n.Else != nil {
|
||||
return EvalRaw(n.Else, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
package terraform
|
||||
|
||||
// EvalNoop is an EvalNode that does nothing.
|
||||
type EvalNoop struct{}
|
||||
|
||||
func (EvalNoop) Eval(EvalContext) (interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
// EvalSequence is an EvalNode that evaluates in sequence.
|
||||
type EvalSequence struct {
|
||||
Nodes []EvalNode
|
||||
}
|
||||
|
||||
func (n *EvalSequence) Eval(ctx EvalContext) (interface{}, error) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
for _, n := range n.Nodes {
|
||||
if n == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err := EvalRaw(n, ctx); err != nil {
|
||||
if _, isEarlyExit := err.(EvalEarlyExitError); isEarlyExit {
|
||||
// In this path we abort early, losing any non-error
|
||||
// diagnostics we saw earlier.
|
||||
return nil, err
|
||||
}
|
||||
diags = diags.Append(err)
|
||||
if diags.HasErrors() {
|
||||
// Halt if we get some errors, but warnings are okay.
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, diags.ErrWithWarnings()
|
||||
}
|
||||
|
||||
// EvalNodeFilterable impl.
|
||||
func (n *EvalSequence) Filter(fn EvalNodeFilterFunc) {
|
||||
for i, node := range n.Nodes {
|
||||
n.Nodes[i] = fn(node)
|
||||
}
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEvalSequence_impl(t *testing.T) {
|
||||
var _ EvalNodeFilterable = new(EvalSequence)
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
package terraform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMockEvalContext_impl(t *testing.T) {
|
||||
var _ EvalContext = new(MockEvalContext)
|
||||
}
|
||||
|
||||
func TestEval(t *testing.T) {
|
||||
var result int
|
||||
n := &testEvalAdd{
|
||||
Items: []int{10, 32},
|
||||
Result: &result,
|
||||
}
|
||||
|
||||
if _, err := Eval(n, nil); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if result != 42 {
|
||||
t.Fatalf("bad: %#v", result)
|
||||
}
|
||||
}
|
||||
|
||||
type testEvalAdd struct {
|
||||
Items []int
|
||||
Result *int
|
||||
}
|
||||
|
||||
func (n *testEvalAdd) Eval(ctx EvalContext) (interface{}, error) {
|
||||
result := 0
|
||||
for _, item := range n.Items {
|
||||
result += item
|
||||
}
|
||||
|
||||
*n.Result = result
|
||||
return nil, nil
|
||||
}
|
||||
Loading…
Reference in new issue