1package terraform
2
3import (
4	"github.com/hashicorp/hcl2/hcl"
5	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
6	"github.com/hashicorp/terraform-plugin-sdk/internal/configs"
7	"github.com/hashicorp/terraform-plugin-sdk/internal/dag"
8	"github.com/hashicorp/terraform-plugin-sdk/internal/lang"
9	"github.com/zclconf/go-cty/cty"
10)
11
12// NodeApplyableModuleVariable represents a module variable input during
13// the apply step.
14type NodeApplyableModuleVariable struct {
15	Addr   addrs.AbsInputVariableInstance
16	Config *configs.Variable // Config is the var in the config
17	Expr   hcl.Expression    // Expr is the value expression given in the call
18}
19
20// Ensure that we are implementing all of the interfaces we think we are
21// implementing.
22var (
23	_ GraphNodeSubPath          = (*NodeApplyableModuleVariable)(nil)
24	_ RemovableIfNotTargeted    = (*NodeApplyableModuleVariable)(nil)
25	_ GraphNodeReferenceOutside = (*NodeApplyableModuleVariable)(nil)
26	_ GraphNodeReferenceable    = (*NodeApplyableModuleVariable)(nil)
27	_ GraphNodeReferencer       = (*NodeApplyableModuleVariable)(nil)
28	_ GraphNodeEvalable         = (*NodeApplyableModuleVariable)(nil)
29	_ dag.GraphNodeDotter       = (*NodeApplyableModuleVariable)(nil)
30)
31
32func (n *NodeApplyableModuleVariable) Name() string {
33	return n.Addr.String()
34}
35
36// GraphNodeSubPath
37func (n *NodeApplyableModuleVariable) Path() addrs.ModuleInstance {
38	// We execute in the parent scope (above our own module) because
39	// expressions in our value are resolved in that context.
40	return n.Addr.Module.Parent()
41}
42
43// RemovableIfNotTargeted
44func (n *NodeApplyableModuleVariable) RemoveIfNotTargeted() bool {
45	// We need to add this so that this node will be removed if
46	// it isn't targeted or a dependency of a target.
47	return true
48}
49
50// GraphNodeReferenceOutside implementation
51func (n *NodeApplyableModuleVariable) ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance) {
52
53	// Module input variables have their value expressions defined in the
54	// context of their calling (parent) module, and so references from
55	// a node of this type should be resolved in the parent module instance.
56	referencePath = n.Addr.Module.Parent()
57
58	// Input variables are _referenced_ from their own module, though.
59	selfPath = n.Addr.Module
60
61	return // uses named return values
62}
63
64// GraphNodeReferenceable
65func (n *NodeApplyableModuleVariable) ReferenceableAddrs() []addrs.Referenceable {
66	return []addrs.Referenceable{n.Addr.Variable}
67}
68
69// GraphNodeReferencer
70func (n *NodeApplyableModuleVariable) References() []*addrs.Reference {
71
72	// If we have no value expression, we cannot depend on anything.
73	if n.Expr == nil {
74		return nil
75	}
76
77	// Variables in the root don't depend on anything, because their values
78	// are gathered prior to the graph walk and recorded in the context.
79	if len(n.Addr.Module) == 0 {
80		return nil
81	}
82
83	// Otherwise, we depend on anything referenced by our value expression.
84	// We ignore diagnostics here under the assumption that we'll re-eval
85	// all these things later and catch them then; for our purposes here,
86	// we only care about valid references.
87	//
88	// Due to our GraphNodeReferenceOutside implementation, the addresses
89	// returned by this function are interpreted in the _parent_ module from
90	// where our associated variable was declared, which is correct because
91	// our value expression is assigned within a "module" block in the parent
92	// module.
93	refs, _ := lang.ReferencesInExpr(n.Expr)
94	return refs
95}
96
97// GraphNodeEvalable
98func (n *NodeApplyableModuleVariable) EvalTree() EvalNode {
99	// If we have no value, do nothing
100	if n.Expr == nil {
101		return &EvalNoop{}
102	}
103
104	// Otherwise, interpolate the value of this variable and set it
105	// within the variables mapping.
106	vals := make(map[string]cty.Value)
107
108	_, call := n.Addr.Module.CallInstance()
109
110	return &EvalSequence{
111		Nodes: []EvalNode{
112			&EvalOpFilter{
113				Ops: []walkOperation{walkRefresh, walkPlan, walkApply,
114					walkDestroy, walkValidate},
115				Node: &EvalModuleCallArgument{
116					Addr:   n.Addr.Variable,
117					Config: n.Config,
118					Expr:   n.Expr,
119					Values: vals,
120
121					IgnoreDiagnostics: false,
122				},
123			},
124
125			&EvalSetModuleCallArguments{
126				Module: call,
127				Values: vals,
128			},
129		},
130	}
131}
132
133// dag.GraphNodeDotter impl.
134func (n *NodeApplyableModuleVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
135	return &dag.DotNode{
136		Name: name,
137		Attrs: map[string]string{
138			"label": n.Name(),
139			"shape": "note",
140		},
141	}
142}
143