diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/addrs/parse_target.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/addrs/parse_target.go | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/addrs/parse_target.go b/vendor/github.com/hashicorp/terraform/addrs/parse_target.go new file mode 100644 index 0000000..057443a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/addrs/parse_target.go | |||
@@ -0,0 +1,318 @@ | |||
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 | } | ||