-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
node_module_variable.go
244 lines (210 loc) · 7.77 KB
/
node_module_variable.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
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/instances"
"github.com/hashicorp/terraform/internal/lang"
"github.com/hashicorp/terraform/internal/tfdiags"
"github.com/zclconf/go-cty/cty"
)
// nodeExpandModuleVariable is the placeholder for an variable that has not yet had
// its module path expanded.
type nodeExpandModuleVariable struct {
Addr addrs.InputVariable
Module addrs.Module
Config *configs.Variable
Expr hcl.Expression
}
var (
_ GraphNodeDynamicExpandable = (*nodeExpandModuleVariable)(nil)
_ GraphNodeReferenceOutside = (*nodeExpandModuleVariable)(nil)
_ GraphNodeReferenceable = (*nodeExpandModuleVariable)(nil)
_ GraphNodeReferencer = (*nodeExpandModuleVariable)(nil)
_ graphNodeTemporaryValue = (*nodeExpandModuleVariable)(nil)
_ graphNodeExpandsInstances = (*nodeExpandModuleVariable)(nil)
)
func (n *nodeExpandModuleVariable) expandsInstances() {}
func (n *nodeExpandModuleVariable) temporaryValue() bool {
return true
}
func (n *nodeExpandModuleVariable) DynamicExpand(ctx EvalContext) (*Graph, error) {
var g Graph
expander := ctx.InstanceExpander()
for _, module := range expander.ExpandModule(n.Module) {
o := &nodeModuleVariable{
Addr: n.Addr.Absolute(module),
Config: n.Config,
Expr: n.Expr,
ModuleInstance: module,
}
g.Add(o)
}
addRootNodeToGraph(&g)
return &g, nil
}
func (n *nodeExpandModuleVariable) Name() string {
return fmt.Sprintf("%s.%s (expand)", n.Module, n.Addr.String())
}
// GraphNodeModulePath
func (n *nodeExpandModuleVariable) ModulePath() addrs.Module {
return n.Module
}
// GraphNodeReferencer
func (n *nodeExpandModuleVariable) References() []*addrs.Reference {
// If we have no value expression, we cannot depend on anything.
if n.Expr == nil {
return nil
}
// Variables in the root don't depend on anything, because their values
// are gathered prior to the graph walk and recorded in the context.
if len(n.Module) == 0 {
return nil
}
// Otherwise, we depend on anything referenced by our value expression.
// We ignore diagnostics here under the assumption that we'll re-eval
// all these things later and catch them then; for our purposes here,
// we only care about valid references.
//
// Due to our GraphNodeReferenceOutside implementation, the addresses
// returned by this function are interpreted in the _parent_ module from
// where our associated variable was declared, which is correct because
// our value expression is assigned within a "module" block in the parent
// module.
refs, _ := lang.ReferencesInExpr(n.Expr)
return refs
}
// GraphNodeReferenceOutside implementation
func (n *nodeExpandModuleVariable) ReferenceOutside() (selfPath, referencePath addrs.Module) {
return n.Module, n.Module.Parent()
}
// GraphNodeReferenceable
func (n *nodeExpandModuleVariable) ReferenceableAddrs() []addrs.Referenceable {
return []addrs.Referenceable{n.Addr}
}
// nodeModuleVariable represents a module variable input during
// the apply step.
type nodeModuleVariable struct {
Addr addrs.AbsInputVariableInstance
Config *configs.Variable // Config is the var in the config
Expr hcl.Expression // Expr is the value expression given in the call
// ModuleInstance in order to create the appropriate context for evaluating
// ModuleCallArguments, ex. so count.index and each.key can resolve
ModuleInstance addrs.ModuleInstance
}
// Ensure that we are implementing all of the interfaces we think we are
// implementing.
var (
_ GraphNodeModuleInstance = (*nodeModuleVariable)(nil)
_ GraphNodeExecutable = (*nodeModuleVariable)(nil)
_ graphNodeTemporaryValue = (*nodeModuleVariable)(nil)
_ dag.GraphNodeDotter = (*nodeModuleVariable)(nil)
)
func (n *nodeModuleVariable) temporaryValue() bool {
return true
}
func (n *nodeModuleVariable) Name() string {
return n.Addr.String()
}
// GraphNodeModuleInstance
func (n *nodeModuleVariable) Path() addrs.ModuleInstance {
// We execute in the parent scope (above our own module) because
// expressions in our value are resolved in that context.
return n.Addr.Module.Parent()
}
// GraphNodeModulePath
func (n *nodeModuleVariable) ModulePath() addrs.Module {
return n.Addr.Module.Module()
}
// GraphNodeExecutable
func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
log.Printf("[TRACE] nodeModuleVariable: evaluating %s", n.Addr)
var val cty.Value
var err error
switch op {
case walkValidate:
val, err = n.evalModuleVariable(ctx, true)
diags = diags.Append(err)
default:
val, err = n.evalModuleVariable(ctx, false)
diags = diags.Append(err)
}
if diags.HasErrors() {
return diags
}
// Set values for arguments of a child module call, for later retrieval
// during expression evaluation.
_, call := n.Addr.Module.CallInstance()
ctx.SetModuleCallArgument(call, n.Addr.Variable, val)
return evalVariableValidations(n.Addr, n.Config, n.Expr, ctx)
}
// dag.GraphNodeDotter impl.
func (n *nodeModuleVariable) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
return &dag.DotNode{
Name: name,
Attrs: map[string]string{
"label": n.Name(),
"shape": "note",
},
}
}
// evalModuleVariable produces the value for a particular variable as will
// be used by a child module instance.
//
// The result is written into a map, with its key set to the local name of the
// variable, disregarding the module instance address. A map is returned instead
// of a single value as a result of trying to be convenient for use with
// EvalContext.SetModuleCallArguments, which expects a map to merge in with any
// existing arguments.
//
// validateOnly indicates that this evaluation is only for config
// validation, and we will not have any expansion module instance
// repetition data.
func (n *nodeModuleVariable) evalModuleVariable(ctx EvalContext, validateOnly bool) (cty.Value, error) {
var diags tfdiags.Diagnostics
var givenVal cty.Value
var errSourceRange tfdiags.SourceRange
if expr := n.Expr; expr != nil {
var moduleInstanceRepetitionData instances.RepetitionData
switch {
case validateOnly:
// the instance expander does not track unknown expansion values, so we
// have to assume all RepetitionData is unknown.
moduleInstanceRepetitionData = instances.RepetitionData{
CountIndex: cty.UnknownVal(cty.Number),
EachKey: cty.UnknownVal(cty.String),
EachValue: cty.DynamicVal,
}
default:
// Get the repetition data for this module instance,
// so we can create the appropriate scope for evaluating our expression
moduleInstanceRepetitionData = ctx.InstanceExpander().GetModuleInstanceRepetitionData(n.ModuleInstance)
}
scope := ctx.EvaluationScope(nil, moduleInstanceRepetitionData)
val, moreDiags := scope.EvalExpr(expr, cty.DynamicPseudoType)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
return cty.DynamicVal, diags.ErrWithWarnings()
}
givenVal = val
errSourceRange = tfdiags.SourceRangeFromHCL(expr.Range())
} else {
// We'll use cty.NilVal to represent the variable not being set at all.
givenVal = cty.NilVal
errSourceRange = tfdiags.SourceRangeFromHCL(n.Config.DeclRange) // we use the declaration range as a fallback for an undefined variable
}
// We construct a synthetic InputValue here to pretend as if this were
// a root module variable set from outside, just as a convenience so we
// can reuse the InputValue type for this.
rawVal := &InputValue{
Value: givenVal,
SourceType: ValueFromConfig,
SourceRange: errSourceRange,
}
finalVal, moreDiags := prepareFinalInputVariableValue(n.Addr, rawVal, n.Config)
diags = diags.Append(moreDiags)
return finalVal, diags.ErrWithWarnings()
}