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