]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/addrs/parse_target.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / addrs / parse_target.go
1 package addrs
2
3 import (
4 "fmt"
5
6 "github.com/hashicorp/hcl2/hcl/hclsyntax"
7
8 "github.com/hashicorp/hcl2/hcl"
9 "github.com/hashicorp/terraform/tfdiags"
10 )
11
12 // Target describes a targeted address with source location information.
13 type Target struct {
14 Subject Targetable
15 SourceRange tfdiags.SourceRange
16 }
17
18 // ParseTarget attempts to interpret the given traversal as a targetable
19 // address. The given traversal must be absolute, or this function will
20 // panic.
21 //
22 // If no error diagnostics are returned, the returned target includes the
23 // address that was extracted and the source range it was extracted from.
24 //
25 // If error diagnostics are returned then the Target value is invalid and
26 // must not be used.
27 func ParseTarget(traversal hcl.Traversal) (*Target, tfdiags.Diagnostics) {
28 path, remain, diags := parseModuleInstancePrefix(traversal)
29 if diags.HasErrors() {
30 return nil, diags
31 }
32
33 rng := tfdiags.SourceRangeFromHCL(traversal.SourceRange())
34
35 if len(remain) == 0 {
36 return &Target{
37 Subject: path,
38 SourceRange: rng,
39 }, diags
40 }
41
42 mode := ManagedResourceMode
43 if remain.RootName() == "data" {
44 mode = DataResourceMode
45 remain = remain[1:]
46 }
47
48 if len(remain) < 2 {
49 diags = diags.Append(&hcl.Diagnostic{
50 Severity: hcl.DiagError,
51 Summary: "Invalid address",
52 Detail: "Resource specification must include a resource type and name.",
53 Subject: remain.SourceRange().Ptr(),
54 })
55 return nil, diags
56 }
57
58 var typeName, name string
59 switch tt := remain[0].(type) {
60 case hcl.TraverseRoot:
61 typeName = tt.Name
62 case hcl.TraverseAttr:
63 typeName = tt.Name
64 default:
65 switch mode {
66 case ManagedResourceMode:
67 diags = diags.Append(&hcl.Diagnostic{
68 Severity: hcl.DiagError,
69 Summary: "Invalid address",
70 Detail: "A resource type name is required.",
71 Subject: remain[0].SourceRange().Ptr(),
72 })
73 case DataResourceMode:
74 diags = diags.Append(&hcl.Diagnostic{
75 Severity: hcl.DiagError,
76 Summary: "Invalid address",
77 Detail: "A data source name is required.",
78 Subject: remain[0].SourceRange().Ptr(),
79 })
80 default:
81 panic("unknown mode")
82 }
83 return nil, diags
84 }
85
86 switch tt := remain[1].(type) {
87 case hcl.TraverseAttr:
88 name = tt.Name
89 default:
90 diags = diags.Append(&hcl.Diagnostic{
91 Severity: hcl.DiagError,
92 Summary: "Invalid address",
93 Detail: "A resource name is required.",
94 Subject: remain[1].SourceRange().Ptr(),
95 })
96 return nil, diags
97 }
98
99 var subject Targetable
100 remain = remain[2:]
101 switch len(remain) {
102 case 0:
103 subject = path.Resource(mode, typeName, name)
104 case 1:
105 if tt, ok := remain[0].(hcl.TraverseIndex); ok {
106 key, err := ParseInstanceKey(tt.Key)
107 if err != nil {
108 diags = diags.Append(&hcl.Diagnostic{
109 Severity: hcl.DiagError,
110 Summary: "Invalid address",
111 Detail: fmt.Sprintf("Invalid resource instance key: %s.", err),
112 Subject: remain[0].SourceRange().Ptr(),
113 })
114 return nil, diags
115 }
116
117 subject = path.ResourceInstance(mode, typeName, name, key)
118 } else {
119 diags = diags.Append(&hcl.Diagnostic{
120 Severity: hcl.DiagError,
121 Summary: "Invalid address",
122 Detail: "Resource instance key must be given in square brackets.",
123 Subject: remain[0].SourceRange().Ptr(),
124 })
125 return nil, diags
126 }
127 default:
128 diags = diags.Append(&hcl.Diagnostic{
129 Severity: hcl.DiagError,
130 Summary: "Invalid address",
131 Detail: "Unexpected extra operators after address.",
132 Subject: remain[1].SourceRange().Ptr(),
133 })
134 return nil, diags
135 }
136
137 return &Target{
138 Subject: subject,
139 SourceRange: rng,
140 }, diags
141 }
142
143 // ParseTargetStr is a helper wrapper around ParseTarget that takes a string
144 // and parses it with the HCL native syntax traversal parser before
145 // interpreting it.
146 //
147 // This should be used only in specialized situations since it will cause the
148 // created references to not have any meaningful source location information.
149 // If a target string is coming from a source that should be identified in
150 // error messages then the caller should instead parse it directly using a
151 // suitable function from the HCL API and pass the traversal itself to
152 // ParseTarget.
153 //
154 // Error diagnostics are returned if either the parsing fails or the analysis
155 // of the traversal fails. There is no way for the caller to distinguish the
156 // two kinds of diagnostics programmatically. If error diagnostics are returned
157 // the returned target may be nil or incomplete.
158 func ParseTargetStr(str string) (*Target, tfdiags.Diagnostics) {
159 var diags tfdiags.Diagnostics
160
161 traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
162 diags = diags.Append(parseDiags)
163 if parseDiags.HasErrors() {
164 return nil, diags
165 }
166
167 target, targetDiags := ParseTarget(traversal)
168 diags = diags.Append(targetDiags)
169 return target, diags
170 }
171
172 // ParseAbsResource attempts to interpret the given traversal as an absolute
173 // resource address, using the same syntax as expected by ParseTarget.
174 //
175 // If no error diagnostics are returned, the returned target includes the
176 // address that was extracted and the source range it was extracted from.
177 //
178 // If error diagnostics are returned then the AbsResource value is invalid and
179 // must not be used.
180 func ParseAbsResource(traversal hcl.Traversal) (AbsResource, tfdiags.Diagnostics) {
181 addr, diags := ParseTarget(traversal)
182 if diags.HasErrors() {
183 return AbsResource{}, diags
184 }
185
186 switch tt := addr.Subject.(type) {
187
188 case AbsResource:
189 return tt, diags
190
191 case AbsResourceInstance: // Catch likely user error with specialized message
192 // Assume that the last element of the traversal must be the index,
193 // since that's required for a valid resource instance address.
194 indexStep := traversal[len(traversal)-1]
195 diags = diags.Append(&hcl.Diagnostic{
196 Severity: hcl.DiagError,
197 Summary: "Invalid address",
198 Detail: "A resource address is required. This instance key identifies a specific resource instance, which is not expected here.",
199 Subject: indexStep.SourceRange().Ptr(),
200 })
201 return AbsResource{}, diags
202
203 case ModuleInstance: // Catch likely user error with specialized message
204 diags = diags.Append(&hcl.Diagnostic{
205 Severity: hcl.DiagError,
206 Summary: "Invalid address",
207 Detail: "A resource address is required here. The module path must be followed by a resource specification.",
208 Subject: traversal.SourceRange().Ptr(),
209 })
210 return AbsResource{}, diags
211
212 default: // Generic message for other address types
213 diags = diags.Append(&hcl.Diagnostic{
214 Severity: hcl.DiagError,
215 Summary: "Invalid address",
216 Detail: "A resource address is required here.",
217 Subject: traversal.SourceRange().Ptr(),
218 })
219 return AbsResource{}, diags
220
221 }
222 }
223
224 // ParseAbsResourceStr is a helper wrapper around ParseAbsResource that takes a
225 // string and parses it with the HCL native syntax traversal parser before
226 // interpreting it.
227 //
228 // Error diagnostics are returned if either the parsing fails or the analysis
229 // of the traversal fails. There is no way for the caller to distinguish the
230 // two kinds of diagnostics programmatically. If error diagnostics are returned
231 // the returned address may be incomplete.
232 //
233 // Since this function has no context about the source of the given string,
234 // any returned diagnostics will not have meaningful source location
235 // information.
236 func ParseAbsResourceStr(str string) (AbsResource, tfdiags.Diagnostics) {
237 var diags tfdiags.Diagnostics
238
239 traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
240 diags = diags.Append(parseDiags)
241 if parseDiags.HasErrors() {
242 return AbsResource{}, diags
243 }
244
245 addr, addrDiags := ParseAbsResource(traversal)
246 diags = diags.Append(addrDiags)
247 return addr, diags
248 }
249
250 // ParseAbsResourceInstance attempts to interpret the given traversal as an
251 // absolute resource instance address, using the same syntax as expected by
252 // ParseTarget.
253 //
254 // If no error diagnostics are returned, the returned target includes the
255 // address that was extracted and the source range it was extracted from.
256 //
257 // If error diagnostics are returned then the AbsResource value is invalid and
258 // must not be used.
259 func ParseAbsResourceInstance(traversal hcl.Traversal) (AbsResourceInstance, tfdiags.Diagnostics) {
260 addr, diags := ParseTarget(traversal)
261 if diags.HasErrors() {
262 return AbsResourceInstance{}, diags
263 }
264
265 switch tt := addr.Subject.(type) {
266
267 case AbsResource:
268 return tt.Instance(NoKey), diags
269
270 case AbsResourceInstance:
271 return tt, diags
272
273 case ModuleInstance: // Catch likely user error with specialized message
274 diags = diags.Append(&hcl.Diagnostic{
275 Severity: hcl.DiagError,
276 Summary: "Invalid address",
277 Detail: "A resource instance address is required here. The module path must be followed by a resource instance specification.",
278 Subject: traversal.SourceRange().Ptr(),
279 })
280 return AbsResourceInstance{}, diags
281
282 default: // Generic message for other address types
283 diags = diags.Append(&hcl.Diagnostic{
284 Severity: hcl.DiagError,
285 Summary: "Invalid address",
286 Detail: "A resource address is required here.",
287 Subject: traversal.SourceRange().Ptr(),
288 })
289 return AbsResourceInstance{}, diags
290
291 }
292 }
293
294 // ParseAbsResourceInstanceStr is a helper wrapper around
295 // ParseAbsResourceInstance that takes a string and parses it with the HCL
296 // native syntax traversal parser before interpreting it.
297 //
298 // Error diagnostics are returned if either the parsing fails or the analysis
299 // of the traversal fails. There is no way for the caller to distinguish the
300 // two kinds of diagnostics programmatically. If error diagnostics are returned
301 // the returned address may be incomplete.
302 //
303 // Since this function has no context about the source of the given string,
304 // any returned diagnostics will not have meaningful source location
305 // information.
306 func ParseAbsResourceInstanceStr(str string) (AbsResourceInstance, tfdiags.Diagnostics) {
307 var diags tfdiags.Diagnostics
308
309 traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
310 diags = diags.Append(parseDiags)
311 if parseDiags.HasErrors() {
312 return AbsResourceInstance{}, diags
313 }
314
315 addr, addrDiags := ParseAbsResourceInstance(traversal)
316 diags = diags.Append(addrDiags)
317 return addr, diags
318 }