mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-29 10:21:01 -06:00
9ebc3e1cd2
In earlier Terraform versions we had an extra validation step prior to the graph walk which tried to partially validate root module input variable values (just checking their type constraints) and then return error messages which specified as accurately as possible where the value had originally come from. We're now handling that sort of validation exclusively during the graph walk so that we can share the main logic between both root module and child module variable values, but previously that shared code wasn't able to generate such specific information about where the values had originated, because it was adapted from code originally written to only deal with child module variables. Here then we restore a similar level of detail as before, when we're processing root module variables. For child module variables, we use synthetic InputValue objects which state that the value was declared in the configuration, thus causing us to produce a similar sort of error message as we would've before which includes a source range covering the argument expression in the calling module block.
116 lines
3.3 KiB
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",
|
|
},
|
|
}
|
|
}
|