6 "github.com/hashicorp/hcl2/hcl/hclsyntax"
8 "github.com/hashicorp/hcl2/hcl"
9 "github.com/hashicorp/terraform/tfdiags"
12 // Target describes a targeted address with source location information.
15 SourceRange tfdiags.SourceRange
18 // ParseTarget attempts to interpret the given traversal as a targetable
19 // address. The given traversal must be absolute, or this function will
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.
25 // If error diagnostics are returned then the Target value is invalid and
27 func ParseTarget(traversal hcl.Traversal) (*Target, tfdiags.Diagnostics) {
28 path, remain, diags := parseModuleInstancePrefix(traversal)
29 if diags.HasErrors() {
33 rng := tfdiags.SourceRangeFromHCL(traversal.SourceRange())
42 mode := ManagedResourceMode
43 if remain.RootName() == "data" {
44 mode = DataResourceMode
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(),
58 var typeName, name string
59 switch tt := remain[0].(type) {
60 case hcl.TraverseRoot:
62 case hcl.TraverseAttr:
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(),
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(),
86 switch tt := remain[1].(type) {
87 case hcl.TraverseAttr:
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(),
99 var subject Targetable
103 subject = path.Resource(mode, typeName, name)
105 if tt, ok := remain[0].(hcl.TraverseIndex); ok {
106 key, err := ParseInstanceKey(tt.Key)
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(),
117 subject = path.ResourceInstance(mode, typeName, name, key)
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(),
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(),
143 // ParseTargetStr is a helper wrapper around ParseTarget that takes a string
144 // and parses it with the HCL native syntax traversal parser before
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
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
161 traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
162 diags = diags.Append(parseDiags)
163 if parseDiags.HasErrors() {
167 target, targetDiags := ParseTarget(traversal)
168 diags = diags.Append(targetDiags)
172 // ParseAbsResource attempts to interpret the given traversal as an absolute
173 // resource address, using the same syntax as expected by ParseTarget.
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.
178 // If error diagnostics are returned then the AbsResource value is invalid and
180 func ParseAbsResource(traversal hcl.Traversal) (AbsResource, tfdiags.Diagnostics) {
181 addr, diags := ParseTarget(traversal)
182 if diags.HasErrors() {
183 return AbsResource{}, diags
186 switch tt := addr.Subject.(type) {
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(),
201 return AbsResource{}, diags
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(),
210 return AbsResource{}, diags
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(),
219 return AbsResource{}, diags
224 // ParseAbsResourceStr is a helper wrapper around ParseAbsResource that takes a
225 // string and parses it with the HCL native syntax traversal parser before
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.
233 // Since this function has no context about the source of the given string,
234 // any returned diagnostics will not have meaningful source location
236 func ParseAbsResourceStr(str string) (AbsResource, tfdiags.Diagnostics) {
237 var diags tfdiags.Diagnostics
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
245 addr, addrDiags := ParseAbsResource(traversal)
246 diags = diags.Append(addrDiags)
250 // ParseAbsResourceInstance attempts to interpret the given traversal as an
251 // absolute resource instance address, using the same syntax as expected by
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.
257 // If error diagnostics are returned then the AbsResource value is invalid and
259 func ParseAbsResourceInstance(traversal hcl.Traversal) (AbsResourceInstance, tfdiags.Diagnostics) {
260 addr, diags := ParseTarget(traversal)
261 if diags.HasErrors() {
262 return AbsResourceInstance{}, diags
265 switch tt := addr.Subject.(type) {
268 return tt.Instance(NoKey), diags
270 case AbsResourceInstance:
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(),
280 return AbsResourceInstance{}, diags
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(),
289 return AbsResourceInstance{}, diags
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.
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.
303 // Since this function has no context about the source of the given string,
304 // any returned diagnostics will not have meaningful source location
306 func ParseAbsResourceInstanceStr(str string) (AbsResourceInstance, tfdiags.Diagnostics) {
307 var diags tfdiags.Diagnostics
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
315 addr, addrDiags := ParseAbsResourceInstance(traversal)
316 diags = diags.Append(addrDiags)