diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/addrs/module_instance.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/addrs/module_instance.go | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/addrs/module_instance.go b/vendor/github.com/hashicorp/terraform/addrs/module_instance.go new file mode 100644 index 0000000..67e73e5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/addrs/module_instance.go | |||
@@ -0,0 +1,415 @@ | |||
1 | package addrs | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "fmt" | ||
6 | |||
7 | "github.com/hashicorp/hcl2/hcl" | ||
8 | "github.com/hashicorp/hcl2/hcl/hclsyntax" | ||
9 | "github.com/zclconf/go-cty/cty" | ||
10 | "github.com/zclconf/go-cty/cty/gocty" | ||
11 | |||
12 | "github.com/hashicorp/terraform/tfdiags" | ||
13 | ) | ||
14 | |||
15 | // ModuleInstance is an address for a particular module instance within the | ||
16 | // dynamic module tree. This is an extension of the static traversals | ||
17 | // represented by type Module that deals with the possibility of a single | ||
18 | // module call producing multiple instances via the "count" and "for_each" | ||
19 | // arguments. | ||
20 | // | ||
21 | // Although ModuleInstance is a slice, it should be treated as immutable after | ||
22 | // creation. | ||
23 | type ModuleInstance []ModuleInstanceStep | ||
24 | |||
25 | var ( | ||
26 | _ Targetable = ModuleInstance(nil) | ||
27 | ) | ||
28 | |||
29 | func ParseModuleInstance(traversal hcl.Traversal) (ModuleInstance, tfdiags.Diagnostics) { | ||
30 | mi, remain, diags := parseModuleInstancePrefix(traversal) | ||
31 | if len(remain) != 0 { | ||
32 | if len(remain) == len(traversal) { | ||
33 | diags = diags.Append(&hcl.Diagnostic{ | ||
34 | Severity: hcl.DiagError, | ||
35 | Summary: "Invalid module instance address", | ||
36 | Detail: "A module instance address must begin with \"module.\".", | ||
37 | Subject: remain.SourceRange().Ptr(), | ||
38 | }) | ||
39 | } else { | ||
40 | diags = diags.Append(&hcl.Diagnostic{ | ||
41 | Severity: hcl.DiagError, | ||
42 | Summary: "Invalid module instance address", | ||
43 | Detail: "The module instance address is followed by additional invalid content.", | ||
44 | Subject: remain.SourceRange().Ptr(), | ||
45 | }) | ||
46 | } | ||
47 | } | ||
48 | return mi, diags | ||
49 | } | ||
50 | |||
51 | // ParseModuleInstanceStr is a helper wrapper around ParseModuleInstance | ||
52 | // that takes a string and parses it with the HCL native syntax traversal parser | ||
53 | // before interpreting it. | ||
54 | // | ||
55 | // This should be used only in specialized situations since it will cause the | ||
56 | // created references to not have any meaningful source location information. | ||
57 | // If a reference string is coming from a source that should be identified in | ||
58 | // error messages then the caller should instead parse it directly using a | ||
59 | // suitable function from the HCL API and pass the traversal itself to | ||
60 | // ParseProviderConfigCompact. | ||
61 | // | ||
62 | // Error diagnostics are returned if either the parsing fails or the analysis | ||
63 | // of the traversal fails. There is no way for the caller to distinguish the | ||
64 | // two kinds of diagnostics programmatically. If error diagnostics are returned | ||
65 | // then the returned address is invalid. | ||
66 | func ParseModuleInstanceStr(str string) (ModuleInstance, tfdiags.Diagnostics) { | ||
67 | var diags tfdiags.Diagnostics | ||
68 | |||
69 | traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) | ||
70 | diags = diags.Append(parseDiags) | ||
71 | if parseDiags.HasErrors() { | ||
72 | return nil, diags | ||
73 | } | ||
74 | |||
75 | addr, addrDiags := ParseModuleInstance(traversal) | ||
76 | diags = diags.Append(addrDiags) | ||
77 | return addr, diags | ||
78 | } | ||
79 | |||
80 | func parseModuleInstancePrefix(traversal hcl.Traversal) (ModuleInstance, hcl.Traversal, tfdiags.Diagnostics) { | ||
81 | remain := traversal | ||
82 | var mi ModuleInstance | ||
83 | var diags tfdiags.Diagnostics | ||
84 | |||
85 | for len(remain) > 0 { | ||
86 | var next string | ||
87 | switch tt := remain[0].(type) { | ||
88 | case hcl.TraverseRoot: | ||
89 | next = tt.Name | ||
90 | case hcl.TraverseAttr: | ||
91 | next = tt.Name | ||
92 | default: | ||
93 | diags = diags.Append(&hcl.Diagnostic{ | ||
94 | Severity: hcl.DiagError, | ||
95 | Summary: "Invalid address operator", | ||
96 | Detail: "Module address prefix must be followed by dot and then a name.", | ||
97 | Subject: remain[0].SourceRange().Ptr(), | ||
98 | }) | ||
99 | break | ||
100 | } | ||
101 | |||
102 | if next != "module" { | ||
103 | break | ||
104 | } | ||
105 | |||
106 | kwRange := remain[0].SourceRange() | ||
107 | remain = remain[1:] | ||
108 | // If we have the prefix "module" then we should be followed by an | ||
109 | // module call name, as an attribute, and then optionally an index step | ||
110 | // giving the instance key. | ||
111 | if len(remain) == 0 { | ||
112 | diags = diags.Append(&hcl.Diagnostic{ | ||
113 | Severity: hcl.DiagError, | ||
114 | Summary: "Invalid address operator", | ||
115 | Detail: "Prefix \"module.\" must be followed by a module name.", | ||
116 | Subject: &kwRange, | ||
117 | }) | ||
118 | break | ||
119 | } | ||
120 | |||
121 | var moduleName string | ||
122 | switch tt := remain[0].(type) { | ||
123 | case hcl.TraverseAttr: | ||
124 | moduleName = tt.Name | ||
125 | default: | ||
126 | diags = diags.Append(&hcl.Diagnostic{ | ||
127 | Severity: hcl.DiagError, | ||
128 | Summary: "Invalid address operator", | ||
129 | Detail: "Prefix \"module.\" must be followed by a module name.", | ||
130 | Subject: remain[0].SourceRange().Ptr(), | ||
131 | }) | ||
132 | break | ||
133 | } | ||
134 | remain = remain[1:] | ||
135 | step := ModuleInstanceStep{ | ||
136 | Name: moduleName, | ||
137 | } | ||
138 | |||
139 | if len(remain) > 0 { | ||
140 | if idx, ok := remain[0].(hcl.TraverseIndex); ok { | ||
141 | remain = remain[1:] | ||
142 | |||
143 | switch idx.Key.Type() { | ||
144 | case cty.String: | ||
145 | step.InstanceKey = StringKey(idx.Key.AsString()) | ||
146 | case cty.Number: | ||
147 | var idxInt int | ||
148 | err := gocty.FromCtyValue(idx.Key, &idxInt) | ||
149 | if err == nil { | ||
150 | step.InstanceKey = IntKey(idxInt) | ||
151 | } else { | ||
152 | diags = diags.Append(&hcl.Diagnostic{ | ||
153 | Severity: hcl.DiagError, | ||
154 | Summary: "Invalid address operator", | ||
155 | Detail: fmt.Sprintf("Invalid module index: %s.", err), | ||
156 | Subject: idx.SourceRange().Ptr(), | ||
157 | }) | ||
158 | } | ||
159 | default: | ||
160 | // Should never happen, because no other types are allowed in traversal indices. | ||
161 | diags = diags.Append(&hcl.Diagnostic{ | ||
162 | Severity: hcl.DiagError, | ||
163 | Summary: "Invalid address operator", | ||
164 | Detail: "Invalid module key: must be either a string or an integer.", | ||
165 | Subject: idx.SourceRange().Ptr(), | ||
166 | }) | ||
167 | } | ||
168 | } | ||
169 | } | ||
170 | |||
171 | mi = append(mi, step) | ||
172 | } | ||
173 | |||
174 | var retRemain hcl.Traversal | ||
175 | if len(remain) > 0 { | ||
176 | retRemain = make(hcl.Traversal, len(remain)) | ||
177 | copy(retRemain, remain) | ||
178 | // The first element here might be either a TraverseRoot or a | ||
179 | // TraverseAttr, depending on whether we had a module address on the | ||
180 | // front. To make life easier for callers, we'll normalize to always | ||
181 | // start with a TraverseRoot. | ||
182 | if tt, ok := retRemain[0].(hcl.TraverseAttr); ok { | ||
183 | retRemain[0] = hcl.TraverseRoot{ | ||
184 | Name: tt.Name, | ||
185 | SrcRange: tt.SrcRange, | ||
186 | } | ||
187 | } | ||
188 | } | ||
189 | |||
190 | return mi, retRemain, diags | ||
191 | } | ||
192 | |||
193 | // UnkeyedInstanceShim is a shim method for converting a Module address to the | ||
194 | // equivalent ModuleInstance address that assumes that no modules have | ||
195 | // keyed instances. | ||
196 | // | ||
197 | // This is a temporary allowance for the fact that Terraform does not presently | ||
198 | // support "count" and "for_each" on modules, and thus graph building code that | ||
199 | // derives graph nodes from configuration must just assume unkeyed modules | ||
200 | // in order to construct the graph. At a later time when "count" and "for_each" | ||
201 | // support is added for modules, all callers of this method will need to be | ||
202 | // reworked to allow for keyed module instances. | ||
203 | func (m Module) UnkeyedInstanceShim() ModuleInstance { | ||
204 | path := make(ModuleInstance, len(m)) | ||
205 | for i, name := range m { | ||
206 | path[i] = ModuleInstanceStep{Name: name} | ||
207 | } | ||
208 | return path | ||
209 | } | ||
210 | |||
211 | // ModuleInstanceStep is a single traversal step through the dynamic module | ||
212 | // tree. It is used only as part of ModuleInstance. | ||
213 | type ModuleInstanceStep struct { | ||
214 | Name string | ||
215 | InstanceKey InstanceKey | ||
216 | } | ||
217 | |||
218 | // RootModuleInstance is the module instance address representing the root | ||
219 | // module, which is also the zero value of ModuleInstance. | ||
220 | var RootModuleInstance ModuleInstance | ||
221 | |||
222 | // IsRoot returns true if the receiver is the address of the root module instance, | ||
223 | // or false otherwise. | ||
224 | func (m ModuleInstance) IsRoot() bool { | ||
225 | return len(m) == 0 | ||
226 | } | ||
227 | |||
228 | // Child returns the address of a child module instance of the receiver, | ||
229 | // identified by the given name and key. | ||
230 | func (m ModuleInstance) Child(name string, key InstanceKey) ModuleInstance { | ||
231 | ret := make(ModuleInstance, 0, len(m)+1) | ||
232 | ret = append(ret, m...) | ||
233 | return append(ret, ModuleInstanceStep{ | ||
234 | Name: name, | ||
235 | InstanceKey: key, | ||
236 | }) | ||
237 | } | ||
238 | |||
239 | // Parent returns the address of the parent module instance of the receiver, or | ||
240 | // the receiver itself if there is no parent (if it's the root module address). | ||
241 | func (m ModuleInstance) Parent() ModuleInstance { | ||
242 | if len(m) == 0 { | ||
243 | return m | ||
244 | } | ||
245 | return m[:len(m)-1] | ||
246 | } | ||
247 | |||
248 | // String returns a string representation of the receiver, in the format used | ||
249 | // within e.g. user-provided resource addresses. | ||
250 | // | ||
251 | // The address of the root module has the empty string as its representation. | ||
252 | func (m ModuleInstance) String() string { | ||
253 | var buf bytes.Buffer | ||
254 | sep := "" | ||
255 | for _, step := range m { | ||
256 | buf.WriteString(sep) | ||
257 | buf.WriteString("module.") | ||
258 | buf.WriteString(step.Name) | ||
259 | if step.InstanceKey != NoKey { | ||
260 | buf.WriteString(step.InstanceKey.String()) | ||
261 | } | ||
262 | sep = "." | ||
263 | } | ||
264 | return buf.String() | ||
265 | } | ||
266 | |||
267 | // Equal returns true if the receiver and the given other value | ||
268 | // contains the exact same parts. | ||
269 | func (m ModuleInstance) Equal(o ModuleInstance) bool { | ||
270 | return m.String() == o.String() | ||
271 | } | ||
272 | |||
273 | // Less returns true if the receiver should sort before the given other value | ||
274 | // in a sorted list of addresses. | ||
275 | func (m ModuleInstance) Less(o ModuleInstance) bool { | ||
276 | if len(m) != len(o) { | ||
277 | // Shorter path sorts first. | ||
278 | return len(m) < len(o) | ||
279 | } | ||
280 | |||
281 | for i := range m { | ||
282 | mS, oS := m[i], o[i] | ||
283 | switch { | ||
284 | case mS.Name != oS.Name: | ||
285 | return mS.Name < oS.Name | ||
286 | case mS.InstanceKey != oS.InstanceKey: | ||
287 | return InstanceKeyLess(mS.InstanceKey, oS.InstanceKey) | ||
288 | } | ||
289 | } | ||
290 | |||
291 | return false | ||
292 | } | ||
293 | |||
294 | // Ancestors returns a slice containing the receiver and all of its ancestor | ||
295 | // module instances, all the way up to (and including) the root module. | ||
296 | // The result is ordered by depth, with the root module always first. | ||
297 | // | ||
298 | // Since the result always includes the root module, a caller may choose to | ||
299 | // ignore it by slicing the result with [1:]. | ||
300 | func (m ModuleInstance) Ancestors() []ModuleInstance { | ||
301 | ret := make([]ModuleInstance, 0, len(m)+1) | ||
302 | for i := 0; i <= len(m); i++ { | ||
303 | ret = append(ret, m[:i]) | ||
304 | } | ||
305 | return ret | ||
306 | } | ||
307 | |||
308 | // IsAncestor returns true if the receiver is an ancestor of the given | ||
309 | // other value. | ||
310 | func (m ModuleInstance) IsAncestor(o ModuleInstance) bool { | ||
311 | // Longer or equal sized paths means the receiver cannot | ||
312 | // be an ancestor of the given module insatnce. | ||
313 | if len(m) >= len(o) { | ||
314 | return false | ||
315 | } | ||
316 | |||
317 | for i, ms := range m { | ||
318 | if ms.Name != o[i].Name { | ||
319 | return false | ||
320 | } | ||
321 | if ms.InstanceKey != NoKey && ms.InstanceKey != o[i].InstanceKey { | ||
322 | return false | ||
323 | } | ||
324 | } | ||
325 | |||
326 | return true | ||
327 | } | ||
328 | |||
329 | // Call returns the module call address that corresponds to the given module | ||
330 | // instance, along with the address of the module instance that contains it. | ||
331 | // | ||
332 | // There is no call for the root module, so this method will panic if called | ||
333 | // on the root module address. | ||
334 | // | ||
335 | // A single module call can produce potentially many module instances, so the | ||
336 | // result discards any instance key that might be present on the last step | ||
337 | // of the instance. To retain this, use CallInstance instead. | ||
338 | // | ||
339 | // In practice, this just turns the last element of the receiver into a | ||
340 | // ModuleCall and then returns a slice of the receiever that excludes that | ||
341 | // last part. This is just a convenience for situations where a call address | ||
342 | // is required, such as when dealing with *Reference and Referencable values. | ||
343 | func (m ModuleInstance) Call() (ModuleInstance, ModuleCall) { | ||
344 | if len(m) == 0 { | ||
345 | panic("cannot produce ModuleCall for root module") | ||
346 | } | ||
347 | |||
348 | inst, lastStep := m[:len(m)-1], m[len(m)-1] | ||
349 | return inst, ModuleCall{ | ||
350 | Name: lastStep.Name, | ||
351 | } | ||
352 | } | ||
353 | |||
354 | // CallInstance returns the module call instance address that corresponds to | ||
355 | // the given module instance, along with the address of the module instance | ||
356 | // that contains it. | ||
357 | // | ||
358 | // There is no call for the root module, so this method will panic if called | ||
359 | // on the root module address. | ||
360 | // | ||
361 | // In practice, this just turns the last element of the receiver into a | ||
362 | // ModuleCallInstance and then returns a slice of the receiever that excludes | ||
363 | // that last part. This is just a convenience for situations where a call\ | ||
364 | // address is required, such as when dealing with *Reference and Referencable | ||
365 | // values. | ||
366 | func (m ModuleInstance) CallInstance() (ModuleInstance, ModuleCallInstance) { | ||
367 | if len(m) == 0 { | ||
368 | panic("cannot produce ModuleCallInstance for root module") | ||
369 | } | ||
370 | |||
371 | inst, lastStep := m[:len(m)-1], m[len(m)-1] | ||
372 | return inst, ModuleCallInstance{ | ||
373 | Call: ModuleCall{ | ||
374 | Name: lastStep.Name, | ||
375 | }, | ||
376 | Key: lastStep.InstanceKey, | ||
377 | } | ||
378 | } | ||
379 | |||
380 | // TargetContains implements Targetable by returning true if the given other | ||
381 | // address either matches the receiver, is a sub-module-instance of the | ||
382 | // receiver, or is a targetable absolute address within a module that | ||
383 | // is contained within the reciever. | ||
384 | func (m ModuleInstance) TargetContains(other Targetable) bool { | ||
385 | switch to := other.(type) { | ||
386 | |||
387 | case ModuleInstance: | ||
388 | if len(to) < len(m) { | ||
389 | // Can't be contained if the path is shorter | ||
390 | return false | ||
391 | } | ||
392 | // Other is contained if its steps match for the length of our own path. | ||
393 | for i, ourStep := range m { | ||
394 | otherStep := to[i] | ||
395 | if ourStep != otherStep { | ||
396 | return false | ||
397 | } | ||
398 | } | ||
399 | // If we fall out here then the prefixed matched, so it's contained. | ||
400 | return true | ||
401 | |||
402 | case AbsResource: | ||
403 | return m.TargetContains(to.Module) | ||
404 | |||
405 | case AbsResourceInstance: | ||
406 | return m.TargetContains(to.Module) | ||
407 | |||
408 | default: | ||
409 | return false | ||
410 | } | ||
411 | } | ||
412 | |||
413 | func (m ModuleInstance) targetableSigil() { | ||
414 | // ModuleInstance is targetable | ||
415 | } | ||