]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/hashicorp/terraform/config/interpolate.go
deps: github.com/hashicorp/terraform@sdk-v0.11-with-go-modules
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / config / interpolate.go
CommitLineData
bae9f6d2
JC
1package config
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7
15c0b25d
AP
8 "github.com/hashicorp/terraform/tfdiags"
9
bae9f6d2
JC
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.
17type InterpolatedVariable interface {
18 FullKey() string
15c0b25d
AP
19 SourceRange() tfdiags.SourceRange
20}
21
22// varRange can be embedded into an InterpolatedVariable implementation to
23// implement the SourceRange method.
24type varRange struct {
25 rng tfdiags.SourceRange
26}
27
28func (r varRange) SourceRange() tfdiags.SourceRange {
29 return r.rng
30}
31
32func makeVarRange(rng tfdiags.SourceRange) varRange {
33 return varRange{rng}
bae9f6d2
JC
34}
35
36// CountVariable is a variable for referencing information about
37// the count.
38type CountVariable struct {
39 Type CountValueType
40 key string
15c0b25d 41 varRange
bae9f6d2
JC
42}
43
44// CountValueType is the type of the count variable that is referenced.
45type CountValueType byte
46
47const (
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}"
54type ModuleVariable struct {
55 Name string
56 Field string
57 key string
15c0b25d 58 varRange
bae9f6d2
JC
59}
60
61// A PathVariable is a variable that references path information about the
62// module.
63type PathVariable struct {
64 Type PathValueType
65 key string
15c0b25d 66 varRange
bae9f6d2
JC
67}
68
69type PathValueType byte
70
71const (
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}"
80type 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
15c0b25d 90 varRange
bae9f6d2
JC
91}
92
93// SelfVariable is a variable that is referencing the same resource
94// it is running on: "${self.address}"
95type SelfVariable struct {
96 Field string
97
98 key string
15c0b25d 99 varRange
bae9f6d2
JC
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.
105type SimpleVariable struct {
106 Key string
15c0b25d 107 varRange
bae9f6d2
JC
108}
109
110// TerraformVariable is a "terraform."-prefixed variable used to access
111// metadata about the Terraform run.
112type TerraformVariable struct {
113 Field string
114 key string
15c0b25d 115 varRange
bae9f6d2
JC
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}"
121type UserVariable struct {
122 Name string
123 Elem string
124
125 key string
15c0b25d
AP
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}".
131type LocalVariable struct {
132 Name string
133 varRange
bae9f6d2
JC
134}
135
136func 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)
15c0b25d
AP
147 } else if strings.HasPrefix(v, "local.") {
148 return NewLocalVariable(v)
bae9f6d2
JC
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
158func 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
172func (c *CountVariable) FullKey() string {
173 return c.key
174}
175
176func 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
191func (v *ModuleVariable) FullKey() string {
192 return v.key
193}
194
195func (v *ModuleVariable) GoString() string {
196 return fmt.Sprintf("*%#v", *v)
197}
198
199func 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
217func (v *PathVariable) FullKey() string {
218 return v.key
219}
220
221func 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
279func (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
290func (v *ResourceVariable) FullKey() string {
291 return v.key
292}
293
294func 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
304func (v *SelfVariable) FullKey() string {
305 return v.key
306}
307
308func (v *SelfVariable) GoString() string {
309 return fmt.Sprintf("*%#v", *v)
310}
311
312func NewSimpleVariable(key string) (*SimpleVariable, error) {
15c0b25d 313 return &SimpleVariable{Key: key}, nil
bae9f6d2
JC
314}
315
316func (v *SimpleVariable) FullKey() string {
317 return v.Key
318}
319
320func (v *SimpleVariable) GoString() string {
321 return fmt.Sprintf("*%#v", *v)
322}
323
324func NewTerraformVariable(key string) (*TerraformVariable, error) {
325 field := key[len("terraform."):]
326 return &TerraformVariable{
327 Field: field,
328 key: key,
329 }, nil
330}
331
332func (v *TerraformVariable) FullKey() string {
333 return v.key
334}
335
336func (v *TerraformVariable) GoString() string {
337 return fmt.Sprintf("*%#v", *v)
338}
339
340func 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
360func (v *UserVariable) FullKey() string {
361 return v.key
362}
363
364func (v *UserVariable) GoString() string {
365 return fmt.Sprintf("*%#v", *v)
366}
367
15c0b25d
AP
368func 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
379func (v *LocalVariable) FullKey() string {
380 return fmt.Sprintf("local.%s", v.Name)
381}
382
383func (v *LocalVariable) GoString() string {
384 return fmt.Sprintf("*%#v", *v)
385}
386
bae9f6d2
JC
387// DetectVariables takes an AST root and returns all the interpolated
388// variables that are detected in the AST tree.
389func 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}