opentofu/internal/tofu/node_root_variable.go
Christian Mesh 92937113bf
Fix crash during destroy planning due to validation (#830)
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
2023-11-08 18:07:09 -05:00

126 lines
3.6 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tofu
import (
"log"
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/dag"
"github.com/opentofu/opentofu/internal/tfdiags"
)
// 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 OpenTofu
// 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,
}
}
if checkState := ctx.Checks(); checkState.ConfigHasChecks(n.Addr.InModule(addrs.RootModule)) {
ctx.Checks().ReportCheckableObjects(
n.Addr.InModule(addrs.RootModule),
addrs.MakeSet[addrs.Checkable](n.Addr.Absolute(addrs.RootModuleInstance)))
}
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",
},
}
}