]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/addrs/module_instance.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / addrs / module_instance.go
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 }