]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/config/interpolate.go
Initial transfer of provider code
[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/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.
15 type InterpolatedVariable interface {
16 FullKey() string
17 }
18
19 // CountVariable is a variable for referencing information about
20 // the count.
21 type CountVariable struct {
22 Type CountValueType
23 key string
24 }
25
26 // CountValueType is the type of the count variable that is referenced.
27 type CountValueType byte
28
29 const (
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}"
36 type 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.
44 type PathVariable struct {
45 Type PathValueType
46 key string
47 }
48
49 type PathValueType byte
50
51 const (
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}"
60 type 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}"
74 type 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.
83 type SimpleVariable struct {
84 Key string
85 }
86
87 // TerraformVariable is a "terraform."-prefixed variable used to access
88 // metadata about the Terraform run.
89 type 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}"
97 type UserVariable struct {
98 Name string
99 Elem string
100
101 key string
102 }
103
104 func 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
124 func 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
138 func (c *CountVariable) FullKey() string {
139 return c.key
140 }
141
142 func 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
157 func (v *ModuleVariable) FullKey() string {
158 return v.key
159 }
160
161 func (v *ModuleVariable) GoString() string {
162 return fmt.Sprintf("*%#v", *v)
163 }
164
165 func 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
183 func (v *PathVariable) FullKey() string {
184 return v.key
185 }
186
187 func 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
245 func (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
256 func (v *ResourceVariable) FullKey() string {
257 return v.key
258 }
259
260 func 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
270 func (v *SelfVariable) FullKey() string {
271 return v.key
272 }
273
274 func (v *SelfVariable) GoString() string {
275 return fmt.Sprintf("*%#v", *v)
276 }
277
278 func NewSimpleVariable(key string) (*SimpleVariable, error) {
279 return &SimpleVariable{key}, nil
280 }
281
282 func (v *SimpleVariable) FullKey() string {
283 return v.Key
284 }
285
286 func (v *SimpleVariable) GoString() string {
287 return fmt.Sprintf("*%#v", *v)
288 }
289
290 func NewTerraformVariable(key string) (*TerraformVariable, error) {
291 field := key[len("terraform."):]
292 return &TerraformVariable{
293 Field: field,
294 key: key,
295 }, nil
296 }
297
298 func (v *TerraformVariable) FullKey() string {
299 return v.key
300 }
301
302 func (v *TerraformVariable) GoString() string {
303 return fmt.Sprintf("*%#v", *v)
304 }
305
306 func 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
326 func (v *UserVariable) FullKey() string {
327 return v.key
328 }
329
330 func (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.
336 func 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 }