opentofu/terraform/eval.go
Martin Atkins 3ac6e575f1 core: EvalSequence must continue when only warnings are returned
To avoid a massively-disruptive change to how EvalNode works, we're now
"smuggling" warnings through the error return value for these, but this
depends on all of the Eval machinery correctly handling this special case
and continuing evaluation when only warnings are returned.

Previous changes missed EvalSequence as a place where execution halts on
error. Now it will accumulate diagnostics itself, aborting if any of
them are error diagnostics, and then wrap its own result up in an error
to be returned by the main Eval function, which already treats non-fatal
errors as a special case, though now produces an explicit log message
about that situation to make it easier to spot in trace logs.

This also includes a more detailed warning message for the warning about
provider input being disabled. While this warning should be removed before
we release anyway, having this additional detail is helpful in debugging
tests where it's being returned.
2018-10-16 18:49:20 -07:00

71 lines
1.9 KiB
Go

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) {
path := "unknown"
if ctx != nil {
path = ctx.Path().String()
}
if path == "" {
path = "<root>"
}
log.Printf("[TRACE] %s: eval: %T", path, n)
output, err := n.Eval(ctx)
if err != nil {
switch err.(type) {
case EvalEarlyExitError:
log.Printf("[TRACE] %s: eval: %T, early exit err: %s", path, n, err)
case tfdiags.NonFatalError:
log.Printf("[WARN] %s: eval: %T, non-fatal err: %s", path, n, err)
default:
log.Printf("[ERROR] %s: eval: %T, err: %s", path, n, err)
}
}
return output, err
}