blob: f194b9cc82a28d14c957d698a18609550aecd955 [file] [log] [blame]
package terraform
import (
"fmt"
"log"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/dag"
"github.com/hashicorp/terraform/internal/lang"
"github.com/hashicorp/terraform/internal/tfdiags"
"github.com/zclconf/go-cty/cty"
)
// nodeExpandLocal represents a named local value in a configuration module,
// which has not yet been expanded.
type nodeExpandLocal struct {
Addr addrs.LocalValue
Module addrs.Module
Config *configs.Local
}
var (
_ GraphNodeReferenceable = (*nodeExpandLocal)(nil)
_ GraphNodeReferencer = (*nodeExpandLocal)(nil)
_ GraphNodeDynamicExpandable = (*nodeExpandLocal)(nil)
_ graphNodeTemporaryValue = (*nodeExpandLocal)(nil)
_ graphNodeExpandsInstances = (*nodeExpandLocal)(nil)
)
func (n *nodeExpandLocal) expandsInstances() {}
// graphNodeTemporaryValue
func (n *nodeExpandLocal) temporaryValue() bool {
return true
}
func (n *nodeExpandLocal) Name() string {
path := n.Module.String()
addr := n.Addr.String() + " (expand)"
if path != "" {
return path + "." + addr
}
return addr
}
// GraphNodeModulePath
func (n *nodeExpandLocal) ModulePath() addrs.Module {
return n.Module
}
// GraphNodeReferenceable
func (n *nodeExpandLocal) ReferenceableAddrs() []addrs.Referenceable {
return []addrs.Referenceable{n.Addr}
}
// GraphNodeReferencer
func (n *nodeExpandLocal) References() []*addrs.Reference {
refs, _ := lang.ReferencesInExpr(n.Config.Expr)
return refs
}
func (n *nodeExpandLocal) DynamicExpand(ctx EvalContext) (*Graph, error) {
var g Graph
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Module) {
o := &NodeLocal{
Addr: n.Addr.Absolute(module),
Config: n.Config,
}
log.Printf("[TRACE] Expanding local: adding %s as %T", o.Addr.String(), o)
g.Add(o)
}
addRootNodeToGraph(&g)
return &g, nil
}
// NodeLocal represents a named local value in a particular module.
//
// Local value nodes only have one operation, common to all walk types:
// evaluate the result and place it in state.
type NodeLocal struct {
Addr addrs.AbsLocalValue
Config *configs.Local
}
var (
_ GraphNodeModuleInstance = (*NodeLocal)(nil)
_ GraphNodeReferenceable = (*NodeLocal)(nil)
_ GraphNodeReferencer = (*NodeLocal)(nil)
_ GraphNodeExecutable = (*NodeLocal)(nil)
_ graphNodeTemporaryValue = (*NodeLocal)(nil)
_ dag.GraphNodeDotter = (*NodeLocal)(nil)
)
// graphNodeTemporaryValue
func (n *NodeLocal) temporaryValue() bool {
return true
}
func (n *NodeLocal) Name() string {
return n.Addr.String()
}
// GraphNodeModuleInstance
func (n *NodeLocal) Path() addrs.ModuleInstance {
return n.Addr.Module
}
// GraphNodeModulePath
func (n *NodeLocal) ModulePath() addrs.Module {
return n.Addr.Module.Module()
}
// GraphNodeReferenceable
func (n *NodeLocal) ReferenceableAddrs() []addrs.Referenceable {
return []addrs.Referenceable{n.Addr.LocalValue}
}
// GraphNodeReferencer
func (n *NodeLocal) References() []*addrs.Reference {
refs, _ := lang.ReferencesInExpr(n.Config.Expr)
return refs
}
// GraphNodeExecutable
// NodeLocal.Execute is an Execute implementation that evaluates the
// expression for a local value and writes it into a transient part of
// the state.
func (n *NodeLocal) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
expr := n.Config.Expr
addr := n.Addr.LocalValue
// We ignore diags here because any problems we might find will be found
// again in EvaluateExpr below.
refs, _ := lang.ReferencesInExpr(expr)
for _, ref := range refs {
if ref.Subject == addr {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Self-referencing local value",
Detail: fmt.Sprintf("Local value %s cannot use its own result as part of its expression.", addr),
Subject: ref.SourceRange.ToHCL().Ptr(),
Context: expr.Range().Ptr(),
})
}
}
if diags.HasErrors() {
return diags
}
val, moreDiags := ctx.EvaluateExpr(expr, cty.DynamicPseudoType, nil)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
return diags
}
state := ctx.State()
if state == nil {
diags = diags.Append(fmt.Errorf("cannot write local value to nil state"))
return diags
}
state.SetLocalValue(addr.Absolute(ctx.Path()), val)
return diags
}
// dag.GraphNodeDotter impl.
func (n *NodeLocal) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": n.Name(),
"shape": "note",
},
}
}