aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/config/interpolate.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config/interpolate.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/config/interpolate.go386
1 files changed, 386 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate.go b/vendor/github.com/hashicorp/terraform/config/interpolate.go
new file mode 100644
index 0000000..bbb3555
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/config/interpolate.go
@@ -0,0 +1,386 @@
1package config
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7
8 "github.com/hashicorp/hil/ast"
9)
10
11// An InterpolatedVariable is a variable reference within an interpolation.
12//
13// Implementations of this interface represents various sources where
14// variables can come from: user variables, resources, etc.
15type InterpolatedVariable interface {
16 FullKey() string
17}
18
19// CountVariable is a variable for referencing information about
20// the count.
21type CountVariable struct {
22 Type CountValueType
23 key string
24}
25
26// CountValueType is the type of the count variable that is referenced.
27type CountValueType byte
28
29const (
30 CountValueInvalid CountValueType = iota
31 CountValueIndex
32)
33
34// A ModuleVariable is a variable that is referencing the output
35// of a module, such as "${module.foo.bar}"
36type ModuleVariable struct {
37 Name string
38 Field string
39 key string
40}
41
42// A PathVariable is a variable that references path information about the
43// module.
44type PathVariable struct {
45 Type PathValueType
46 key string
47}
48
49type PathValueType byte
50
51const (
52 PathValueInvalid PathValueType = iota
53 PathValueCwd
54 PathValueModule
55 PathValueRoot
56)
57
58// A ResourceVariable is a variable that is referencing the field
59// of a resource, such as "${aws_instance.foo.ami}"
60type ResourceVariable struct {
61 Mode ResourceMode
62 Type string // Resource type, i.e. "aws_instance"
63 Name string // Resource name
64 Field string // Resource field
65
66 Multi bool // True if multi-variable: aws_instance.foo.*.id
67 Index int // Index for multi-variable: aws_instance.foo.1.id == 1
68
69 key string
70}
71
72// SelfVariable is a variable that is referencing the same resource
73// it is running on: "${self.address}"
74type SelfVariable struct {
75 Field string
76
77 key string
78}
79
80// SimpleVariable is an unprefixed variable, which can show up when users have
81// strings they are passing down to resources that use interpolation
82// internally. The template_file resource is an example of this.
83type SimpleVariable struct {
84 Key string
85}
86
87// TerraformVariable is a "terraform."-prefixed variable used to access
88// metadata about the Terraform run.
89type TerraformVariable struct {
90 Field string
91 key string
92}
93
94// A UserVariable is a variable that is referencing a user variable
95// that is inputted from outside the configuration. This looks like
96// "${var.foo}"
97type UserVariable struct {
98 Name string
99 Elem string
100
101 key string
102}
103
104func NewInterpolatedVariable(v string) (InterpolatedVariable, error) {
105 if strings.HasPrefix(v, "count.") {
106 return NewCountVariable(v)
107 } else if strings.HasPrefix(v, "path.") {
108 return NewPathVariable(v)
109 } else if strings.HasPrefix(v, "self.") {
110 return NewSelfVariable(v)
111 } else if strings.HasPrefix(v, "terraform.") {
112 return NewTerraformVariable(v)
113 } else if strings.HasPrefix(v, "var.") {
114 return NewUserVariable(v)
115 } else if strings.HasPrefix(v, "module.") {
116 return NewModuleVariable(v)
117 } else if !strings.ContainsRune(v, '.') {
118 return NewSimpleVariable(v)
119 } else {
120 return NewResourceVariable(v)
121 }
122}
123
124func NewCountVariable(key string) (*CountVariable, error) {
125 var fieldType CountValueType
126 parts := strings.SplitN(key, ".", 2)
127 switch parts[1] {
128 case "index":
129 fieldType = CountValueIndex
130 }
131
132 return &CountVariable{
133 Type: fieldType,
134 key: key,
135 }, nil
136}
137
138func (c *CountVariable) FullKey() string {
139 return c.key
140}
141
142func NewModuleVariable(key string) (*ModuleVariable, error) {
143 parts := strings.SplitN(key, ".", 3)
144 if len(parts) < 3 {
145 return nil, fmt.Errorf(
146 "%s: module variables must be three parts: module.name.attr",
147 key)
148 }
149
150 return &ModuleVariable{
151 Name: parts[1],
152 Field: parts[2],
153 key: key,
154 }, nil
155}
156
157func (v *ModuleVariable) FullKey() string {
158 return v.key
159}
160
161func (v *ModuleVariable) GoString() string {
162 return fmt.Sprintf("*%#v", *v)
163}
164
165func NewPathVariable(key string) (*PathVariable, error) {
166 var fieldType PathValueType
167 parts := strings.SplitN(key, ".", 2)
168 switch parts[1] {
169 case "cwd":
170 fieldType = PathValueCwd
171 case "module":
172 fieldType = PathValueModule
173 case "root":
174 fieldType = PathValueRoot
175 }
176
177 return &PathVariable{
178 Type: fieldType,
179 key: key,
180 }, nil
181}
182
183func (v *PathVariable) FullKey() string {
184 return v.key
185}
186
187func NewResourceVariable(key string) (*ResourceVariable, error) {
188 var mode ResourceMode
189 var parts []string
190 if strings.HasPrefix(key, "data.") {
191 mode = DataResourceMode
192 parts = strings.SplitN(key, ".", 4)
193 if len(parts) < 4 {
194 return nil, fmt.Errorf(
195 "%s: data variables must be four parts: data.TYPE.NAME.ATTR",
196 key)
197 }
198
199 // Don't actually need the "data." prefix for parsing, since it's
200 // always constant.
201 parts = parts[1:]
202 } else {
203 mode = ManagedResourceMode
204 parts = strings.SplitN(key, ".", 3)
205 if len(parts) < 3 {
206 return nil, fmt.Errorf(
207 "%s: resource variables must be three parts: TYPE.NAME.ATTR",
208 key)
209 }
210 }
211
212 field := parts[2]
213 multi := false
214 var index int
215
216 if idx := strings.Index(field, "."); idx != -1 {
217 indexStr := field[:idx]
218 multi = indexStr == "*"
219 index = -1
220
221 if !multi {
222 indexInt, err := strconv.ParseInt(indexStr, 0, 0)
223 if err == nil {
224 multi = true
225 index = int(indexInt)
226 }
227 }
228
229 if multi {
230 field = field[idx+1:]
231 }
232 }
233
234 return &ResourceVariable{
235 Mode: mode,
236 Type: parts[0],
237 Name: parts[1],
238 Field: field,
239 Multi: multi,
240 Index: index,
241 key: key,
242 }, nil
243}
244
245func (v *ResourceVariable) ResourceId() string {
246 switch v.Mode {
247 case ManagedResourceMode:
248 return fmt.Sprintf("%s.%s", v.Type, v.Name)
249 case DataResourceMode:
250 return fmt.Sprintf("data.%s.%s", v.Type, v.Name)
251 default:
252 panic(fmt.Errorf("unknown resource mode %s", v.Mode))
253 }
254}
255
256func (v *ResourceVariable) FullKey() string {
257 return v.key
258}
259
260func NewSelfVariable(key string) (*SelfVariable, error) {
261 field := key[len("self."):]
262
263 return &SelfVariable{
264 Field: field,
265
266 key: key,
267 }, nil
268}
269
270func (v *SelfVariable) FullKey() string {
271 return v.key
272}
273
274func (v *SelfVariable) GoString() string {
275 return fmt.Sprintf("*%#v", *v)
276}
277
278func NewSimpleVariable(key string) (*SimpleVariable, error) {
279 return &SimpleVariable{key}, nil
280}
281
282func (v *SimpleVariable) FullKey() string {
283 return v.Key
284}
285
286func (v *SimpleVariable) GoString() string {
287 return fmt.Sprintf("*%#v", *v)
288}
289
290func NewTerraformVariable(key string) (*TerraformVariable, error) {
291 field := key[len("terraform."):]
292 return &TerraformVariable{
293 Field: field,
294 key: key,
295 }, nil
296}
297
298func (v *TerraformVariable) FullKey() string {
299 return v.key
300}
301
302func (v *TerraformVariable) GoString() string {
303 return fmt.Sprintf("*%#v", *v)
304}
305
306func NewUserVariable(key string) (*UserVariable, error) {
307 name := key[len("var."):]
308 elem := ""
309 if idx := strings.Index(name, "."); idx > -1 {
310 elem = name[idx+1:]
311 name = name[:idx]
312 }
313
314 if len(elem) > 0 {
315 return nil, fmt.Errorf("Invalid dot index found: 'var.%s.%s'. Values in maps and lists can be referenced using square bracket indexing, like: 'var.mymap[\"key\"]' or 'var.mylist[1]'.", name, elem)
316 }
317
318 return &UserVariable{
319 key: key,
320
321 Name: name,
322 Elem: elem,
323 }, nil
324}
325
326func (v *UserVariable) FullKey() string {
327 return v.key
328}
329
330func (v *UserVariable) GoString() string {
331 return fmt.Sprintf("*%#v", *v)
332}
333
334// DetectVariables takes an AST root and returns all the interpolated
335// variables that are detected in the AST tree.
336func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) {
337 var result []InterpolatedVariable
338 var resultErr error
339
340 // Visitor callback
341 fn := func(n ast.Node) ast.Node {
342 if resultErr != nil {
343 return n
344 }
345
346 switch vn := n.(type) {
347 case *ast.VariableAccess:
348 v, err := NewInterpolatedVariable(vn.Name)
349 if err != nil {
350 resultErr = err
351 return n
352 }
353 result = append(result, v)
354 case *ast.Index:
355 if va, ok := vn.Target.(*ast.VariableAccess); ok {
356 v, err := NewInterpolatedVariable(va.Name)
357 if err != nil {
358 resultErr = err
359 return n
360 }
361 result = append(result, v)
362 }
363 if va, ok := vn.Key.(*ast.VariableAccess); ok {
364 v, err := NewInterpolatedVariable(va.Name)
365 if err != nil {
366 resultErr = err
367 return n
368 }
369 result = append(result, v)
370 }
371 default:
372 return n
373 }
374
375 return n
376 }
377
378 // Visitor pattern
379 root.Accept(fn)
380
381 if resultErr != nil {
382 return nil, resultErr
383 }
384
385 return result, nil
386}