terraform/internal/terraform/node_root_variable.go

116 lines
3.3 KiB
Go

package terraform
import (
"log"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/dag"
"github.com/hashicorp/terraform/internal/tfdiags"
"github.com/zclconf/go-cty/cty"
)
// NodeRootVariable represents a root variable input.
type NodeRootVariable struct {
Addr addrs.InputVariable
Config *configs.Variable
// RawValue is the value for the variable set from outside Terraform
// Core, such as on the command line, or from an environment variable,
// or similar. This is the raw value that was provided, not yet
// converted or validated, and can be nil for a variable that isn't
// set at all.
RawValue *InputValue
}
var (
_ GraphNodeModuleInstance = (*NodeRootVariable)(nil)
_ GraphNodeReferenceable = (*NodeRootVariable)(nil)
)
func (n *NodeRootVariable) Name() string {
return n.Addr.String()
}
// GraphNodeModuleInstance
func (n *NodeRootVariable) Path() addrs.ModuleInstance {
return addrs.RootModuleInstance
}
func (n *NodeRootVariable) ModulePath() addrs.Module {
return addrs.RootModule
}
// GraphNodeReferenceable
func (n *NodeRootVariable) ReferenceableAddrs() []addrs.Referenceable {
return []addrs.Referenceable{n.Addr}
}
// GraphNodeExecutable
func (n *NodeRootVariable) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics {
// Root module variables are special in that they are provided directly
// by the caller (usually, the CLI layer) and so we don't really need to
// evaluate them in the usual sense, but we do need to process the raw
// values given by the caller to match what the module is expecting, and
// make sure the values are valid.
var diags tfdiags.Diagnostics
addr := addrs.RootModuleInstance.InputVariable(n.Addr.Name)
log.Printf("[TRACE] NodeRootVariable: evaluating %s", addr)
if n.Config == nil {
// Because we build NodeRootVariable from configuration in the normal
// case it's strange to get here, but we tolerate it to allow for
// tests that might not populate the inputs fully.
return nil
}
givenVal := n.RawValue
if givenVal == nil {
// We'll use cty.NilVal to represent the variable not being set at
// all, which for historical reasons is unfortunately different than
// explicitly setting it to null in some cases. In normal code we
// should never get here because all variables should have raw
// values, but we can get here in some historical tests that call
// in directly and don't necessarily obey the rules.
givenVal = &InputValue{
Value: cty.NilVal,
SourceType: ValueFromUnknown,
}
}
finalVal, moreDiags := prepareFinalInputVariableValue(
addr,
givenVal,
n.Config,
)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
// No point in proceeding to validations then, because they'll
// probably fail trying to work with a value of the wrong type.
return diags
}
ctx.SetRootModuleArgument(addr.Variable, finalVal)
moreDiags = evalVariableValidations(
addrs.RootModuleInstance.InputVariable(n.Addr.Name),
n.Config,
nil, // not set for root module variables
ctx,
)
diags = diags.Append(moreDiags)
return diags
}
// dag.GraphNodeDotter impl.
func (n *NodeRootVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": n.Name(),
"shape": "note",
},
}
}