github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/eval_variable.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/hcl/v2"
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
     9  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs"
    10  	"github.com/zclconf/go-cty/cty"
    11  	"github.com/zclconf/go-cty/cty/convert"
    12  )
    13  
    14  // EvalSetModuleCallArguments is an EvalNode implementation that sets values
    15  // for arguments of a child module call, for later retrieval during
    16  // expression evaluation.
    17  type EvalSetModuleCallArguments struct {
    18  	Module addrs.ModuleCallInstance
    19  	Values map[string]cty.Value
    20  }
    21  
    22  // TODO: test
    23  func (n *EvalSetModuleCallArguments) Eval(ctx EvalContext) (interface{}, error) {
    24  	ctx.SetModuleCallArguments(n.Module, n.Values)
    25  	return nil, nil
    26  }
    27  
    28  // EvalModuleCallArgument is an EvalNode implementation that produces the value
    29  // for a particular variable as will be used by a child module instance.
    30  //
    31  // The result is written into the map given in Values, with its key
    32  // set to the local name of the variable, disregarding the module instance
    33  // address. Any existing values in that map are deleted first. This weird
    34  // interface is a result of trying to be convenient for use with
    35  // EvalContext.SetModuleCallArguments, which expects a map to merge in with
    36  // any existing arguments.
    37  type EvalModuleCallArgument struct {
    38  	Addr   addrs.InputVariable
    39  	Config *configs.Variable
    40  	Expr   hcl.Expression
    41  
    42  	// If this flag is set, any diagnostics are discarded and this operation
    43  	// will always succeed, though may produce an unknown value in the
    44  	// event of an error.
    45  	IgnoreDiagnostics bool
    46  
    47  	Values map[string]cty.Value
    48  }
    49  
    50  func (n *EvalModuleCallArgument) Eval(ctx EvalContext) (interface{}, error) {
    51  	// Clear out the existing mapping
    52  	for k := range n.Values {
    53  		delete(n.Values, k)
    54  	}
    55  
    56  	wantType := n.Config.Type
    57  	name := n.Addr.Name
    58  	expr := n.Expr
    59  
    60  	if expr == nil {
    61  		// Should never happen, but we'll bail out early here rather than
    62  		// crash in case it does. We set no value at all in this case,
    63  		// making a subsequent call to EvalContext.SetModuleCallArguments
    64  		// a no-op.
    65  		log.Printf("[ERROR] attempt to evaluate %s with nil expression", n.Addr.String())
    66  		return nil, nil
    67  	}
    68  
    69  	val, diags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil)
    70  
    71  	// We intentionally passed DynamicPseudoType to EvaluateExpr above because
    72  	// now we can do our own local type conversion and produce an error message
    73  	// with better context if it fails.
    74  	var convErr error
    75  	val, convErr = convert.Convert(val, wantType)
    76  	if convErr != nil {
    77  		diags = diags.Append(&hcl.Diagnostic{
    78  			Severity: hcl.DiagError,
    79  			Summary:  "Invalid value for module argument",
    80  			Detail: fmt.Sprintf(
    81  				"The given value is not suitable for child module variable %q defined at %s: %s.",
    82  				name, n.Config.DeclRange.String(), convErr,
    83  			),
    84  			Subject: expr.Range().Ptr(),
    85  		})
    86  		// We'll return a placeholder unknown value to avoid producing
    87  		// redundant downstream errors.
    88  		val = cty.UnknownVal(wantType)
    89  	}
    90  
    91  	n.Values[name] = val
    92  	if n.IgnoreDiagnostics {
    93  		return nil, nil
    94  	}
    95  	return nil, diags.ErrWithWarnings()
    96  }