diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/configs/resource.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/configs/resource.go | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/configs/resource.go b/vendor/github.com/hashicorp/terraform/configs/resource.go new file mode 100644 index 0000000..de1a343 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/configs/resource.go | |||
@@ -0,0 +1,486 @@ | |||
1 | package configs | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/hcl2/gohcl" | ||
7 | "github.com/hashicorp/hcl2/hcl" | ||
8 | "github.com/hashicorp/hcl2/hcl/hclsyntax" | ||
9 | |||
10 | "github.com/hashicorp/terraform/addrs" | ||
11 | ) | ||
12 | |||
13 | // Resource represents a "resource" or "data" block in a module or file. | ||
14 | type Resource struct { | ||
15 | Mode addrs.ResourceMode | ||
16 | Name string | ||
17 | Type string | ||
18 | Config hcl.Body | ||
19 | Count hcl.Expression | ||
20 | ForEach hcl.Expression | ||
21 | |||
22 | ProviderConfigRef *ProviderConfigRef | ||
23 | |||
24 | DependsOn []hcl.Traversal | ||
25 | |||
26 | // Managed is populated only for Mode = addrs.ManagedResourceMode, | ||
27 | // containing the additional fields that apply to managed resources. | ||
28 | // For all other resource modes, this field is nil. | ||
29 | Managed *ManagedResource | ||
30 | |||
31 | DeclRange hcl.Range | ||
32 | TypeRange hcl.Range | ||
33 | } | ||
34 | |||
35 | // ManagedResource represents a "resource" block in a module or file. | ||
36 | type ManagedResource struct { | ||
37 | Connection *Connection | ||
38 | Provisioners []*Provisioner | ||
39 | |||
40 | CreateBeforeDestroy bool | ||
41 | PreventDestroy bool | ||
42 | IgnoreChanges []hcl.Traversal | ||
43 | IgnoreAllChanges bool | ||
44 | |||
45 | CreateBeforeDestroySet bool | ||
46 | PreventDestroySet bool | ||
47 | } | ||
48 | |||
49 | func (r *Resource) moduleUniqueKey() string { | ||
50 | return r.Addr().String() | ||
51 | } | ||
52 | |||
53 | // Addr returns a resource address for the receiver that is relative to the | ||
54 | // resource's containing module. | ||
55 | func (r *Resource) Addr() addrs.Resource { | ||
56 | return addrs.Resource{ | ||
57 | Mode: r.Mode, | ||
58 | Type: r.Type, | ||
59 | Name: r.Name, | ||
60 | } | ||
61 | } | ||
62 | |||
63 | // ProviderConfigAddr returns the address for the provider configuration | ||
64 | // that should be used for this resource. This function implements the | ||
65 | // default behavior of extracting the type from the resource type name if | ||
66 | // an explicit "provider" argument was not provided. | ||
67 | func (r *Resource) ProviderConfigAddr() addrs.ProviderConfig { | ||
68 | if r.ProviderConfigRef == nil { | ||
69 | return r.Addr().DefaultProviderConfig() | ||
70 | } | ||
71 | |||
72 | return addrs.ProviderConfig{ | ||
73 | Type: r.ProviderConfigRef.Name, | ||
74 | Alias: r.ProviderConfigRef.Alias, | ||
75 | } | ||
76 | } | ||
77 | |||
78 | func decodeResourceBlock(block *hcl.Block) (*Resource, hcl.Diagnostics) { | ||
79 | r := &Resource{ | ||
80 | Mode: addrs.ManagedResourceMode, | ||
81 | Type: block.Labels[0], | ||
82 | Name: block.Labels[1], | ||
83 | DeclRange: block.DefRange, | ||
84 | TypeRange: block.LabelRanges[0], | ||
85 | Managed: &ManagedResource{}, | ||
86 | } | ||
87 | |||
88 | content, remain, diags := block.Body.PartialContent(resourceBlockSchema) | ||
89 | r.Config = remain | ||
90 | |||
91 | if !hclsyntax.ValidIdentifier(r.Type) { | ||
92 | diags = append(diags, &hcl.Diagnostic{ | ||
93 | Severity: hcl.DiagError, | ||
94 | Summary: "Invalid resource type name", | ||
95 | Detail: badIdentifierDetail, | ||
96 | Subject: &block.LabelRanges[0], | ||
97 | }) | ||
98 | } | ||
99 | if !hclsyntax.ValidIdentifier(r.Name) { | ||
100 | diags = append(diags, &hcl.Diagnostic{ | ||
101 | Severity: hcl.DiagError, | ||
102 | Summary: "Invalid resource name", | ||
103 | Detail: badIdentifierDetail, | ||
104 | Subject: &block.LabelRanges[1], | ||
105 | }) | ||
106 | } | ||
107 | |||
108 | if attr, exists := content.Attributes["count"]; exists { | ||
109 | r.Count = attr.Expr | ||
110 | } | ||
111 | |||
112 | if attr, exists := content.Attributes["for_each"]; exists { | ||
113 | r.ForEach = attr.Expr | ||
114 | // We currently parse this, but don't yet do anything with it. | ||
115 | diags = append(diags, &hcl.Diagnostic{ | ||
116 | Severity: hcl.DiagError, | ||
117 | Summary: "Reserved argument name in resource block", | ||
118 | Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), | ||
119 | Subject: &attr.NameRange, | ||
120 | }) | ||
121 | } | ||
122 | |||
123 | if attr, exists := content.Attributes["provider"]; exists { | ||
124 | var providerDiags hcl.Diagnostics | ||
125 | r.ProviderConfigRef, providerDiags = decodeProviderConfigRef(attr.Expr, "provider") | ||
126 | diags = append(diags, providerDiags...) | ||
127 | } | ||
128 | |||
129 | if attr, exists := content.Attributes["depends_on"]; exists { | ||
130 | deps, depsDiags := decodeDependsOn(attr) | ||
131 | diags = append(diags, depsDiags...) | ||
132 | r.DependsOn = append(r.DependsOn, deps...) | ||
133 | } | ||
134 | |||
135 | var seenLifecycle *hcl.Block | ||
136 | var seenConnection *hcl.Block | ||
137 | for _, block := range content.Blocks { | ||
138 | switch block.Type { | ||
139 | case "lifecycle": | ||
140 | if seenLifecycle != nil { | ||
141 | diags = append(diags, &hcl.Diagnostic{ | ||
142 | Severity: hcl.DiagError, | ||
143 | Summary: "Duplicate lifecycle block", | ||
144 | Detail: fmt.Sprintf("This resource already has a lifecycle block at %s.", seenLifecycle.DefRange), | ||
145 | Subject: &block.DefRange, | ||
146 | }) | ||
147 | continue | ||
148 | } | ||
149 | seenLifecycle = block | ||
150 | |||
151 | lcContent, lcDiags := block.Body.Content(resourceLifecycleBlockSchema) | ||
152 | diags = append(diags, lcDiags...) | ||
153 | |||
154 | if attr, exists := lcContent.Attributes["create_before_destroy"]; exists { | ||
155 | valDiags := gohcl.DecodeExpression(attr.Expr, nil, &r.Managed.CreateBeforeDestroy) | ||
156 | diags = append(diags, valDiags...) | ||
157 | r.Managed.CreateBeforeDestroySet = true | ||
158 | } | ||
159 | |||
160 | if attr, exists := lcContent.Attributes["prevent_destroy"]; exists { | ||
161 | valDiags := gohcl.DecodeExpression(attr.Expr, nil, &r.Managed.PreventDestroy) | ||
162 | diags = append(diags, valDiags...) | ||
163 | r.Managed.PreventDestroySet = true | ||
164 | } | ||
165 | |||
166 | if attr, exists := lcContent.Attributes["ignore_changes"]; exists { | ||
167 | |||
168 | // ignore_changes can either be a list of relative traversals | ||
169 | // or it can be just the keyword "all" to ignore changes to this | ||
170 | // resource entirely. | ||
171 | // ignore_changes = [ami, instance_type] | ||
172 | // ignore_changes = all | ||
173 | // We also allow two legacy forms for compatibility with earlier | ||
174 | // versions: | ||
175 | // ignore_changes = ["ami", "instance_type"] | ||
176 | // ignore_changes = ["*"] | ||
177 | |||
178 | kw := hcl.ExprAsKeyword(attr.Expr) | ||
179 | |||
180 | switch { | ||
181 | case kw == "all": | ||
182 | r.Managed.IgnoreAllChanges = true | ||
183 | default: | ||
184 | exprs, listDiags := hcl.ExprList(attr.Expr) | ||
185 | diags = append(diags, listDiags...) | ||
186 | |||
187 | var ignoreAllRange hcl.Range | ||
188 | |||
189 | for _, expr := range exprs { | ||
190 | |||
191 | // our expr might be the literal string "*", which | ||
192 | // we accept as a deprecated way of saying "all". | ||
193 | if shimIsIgnoreChangesStar(expr) { | ||
194 | r.Managed.IgnoreAllChanges = true | ||
195 | ignoreAllRange = expr.Range() | ||
196 | diags = append(diags, &hcl.Diagnostic{ | ||
197 | Severity: hcl.DiagWarning, | ||
198 | Summary: "Deprecated ignore_changes wildcard", | ||
199 | Detail: "The [\"*\"] form of ignore_changes wildcard is deprecated. Use \"ignore_changes = all\" to ignore changes to all attributes.", | ||
200 | Subject: attr.Expr.Range().Ptr(), | ||
201 | }) | ||
202 | continue | ||
203 | } | ||
204 | |||
205 | expr, shimDiags := shimTraversalInString(expr, false) | ||
206 | diags = append(diags, shimDiags...) | ||
207 | |||
208 | traversal, travDiags := hcl.RelTraversalForExpr(expr) | ||
209 | diags = append(diags, travDiags...) | ||
210 | if len(traversal) != 0 { | ||
211 | r.Managed.IgnoreChanges = append(r.Managed.IgnoreChanges, traversal) | ||
212 | } | ||
213 | } | ||
214 | |||
215 | if r.Managed.IgnoreAllChanges && len(r.Managed.IgnoreChanges) != 0 { | ||
216 | diags = append(diags, &hcl.Diagnostic{ | ||
217 | Severity: hcl.DiagError, | ||
218 | Summary: "Invalid ignore_changes ruleset", | ||
219 | Detail: "Cannot mix wildcard string \"*\" with non-wildcard references.", | ||
220 | Subject: &ignoreAllRange, | ||
221 | Context: attr.Expr.Range().Ptr(), | ||
222 | }) | ||
223 | } | ||
224 | |||
225 | } | ||
226 | |||
227 | } | ||
228 | |||
229 | case "connection": | ||
230 | if seenConnection != nil { | ||
231 | diags = append(diags, &hcl.Diagnostic{ | ||
232 | Severity: hcl.DiagError, | ||
233 | Summary: "Duplicate connection block", | ||
234 | Detail: fmt.Sprintf("This resource already has a connection block at %s.", seenConnection.DefRange), | ||
235 | Subject: &block.DefRange, | ||
236 | }) | ||
237 | continue | ||
238 | } | ||
239 | seenConnection = block | ||
240 | |||
241 | r.Managed.Connection = &Connection{ | ||
242 | Config: block.Body, | ||
243 | DeclRange: block.DefRange, | ||
244 | } | ||
245 | |||
246 | case "provisioner": | ||
247 | pv, pvDiags := decodeProvisionerBlock(block) | ||
248 | diags = append(diags, pvDiags...) | ||
249 | if pv != nil { | ||
250 | r.Managed.Provisioners = append(r.Managed.Provisioners, pv) | ||
251 | } | ||
252 | |||
253 | default: | ||
254 | // Any other block types are ones we've reserved for future use, | ||
255 | // so they get a generic message. | ||
256 | diags = append(diags, &hcl.Diagnostic{ | ||
257 | Severity: hcl.DiagError, | ||
258 | Summary: "Reserved block type name in resource block", | ||
259 | Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type), | ||
260 | Subject: &block.TypeRange, | ||
261 | }) | ||
262 | } | ||
263 | } | ||
264 | |||
265 | return r, diags | ||
266 | } | ||
267 | |||
268 | func decodeDataBlock(block *hcl.Block) (*Resource, hcl.Diagnostics) { | ||
269 | r := &Resource{ | ||
270 | Mode: addrs.DataResourceMode, | ||
271 | Type: block.Labels[0], | ||
272 | Name: block.Labels[1], | ||
273 | DeclRange: block.DefRange, | ||
274 | TypeRange: block.LabelRanges[0], | ||
275 | } | ||
276 | |||
277 | content, remain, diags := block.Body.PartialContent(dataBlockSchema) | ||
278 | r.Config = remain | ||
279 | |||
280 | if !hclsyntax.ValidIdentifier(r.Type) { | ||
281 | diags = append(diags, &hcl.Diagnostic{ | ||
282 | Severity: hcl.DiagError, | ||
283 | Summary: "Invalid data source name", | ||
284 | Detail: badIdentifierDetail, | ||
285 | Subject: &block.LabelRanges[0], | ||
286 | }) | ||
287 | } | ||
288 | if !hclsyntax.ValidIdentifier(r.Name) { | ||
289 | diags = append(diags, &hcl.Diagnostic{ | ||
290 | Severity: hcl.DiagError, | ||
291 | Summary: "Invalid data resource name", | ||
292 | Detail: badIdentifierDetail, | ||
293 | Subject: &block.LabelRanges[1], | ||
294 | }) | ||
295 | } | ||
296 | |||
297 | if attr, exists := content.Attributes["count"]; exists { | ||
298 | r.Count = attr.Expr | ||
299 | } | ||
300 | |||
301 | if attr, exists := content.Attributes["for_each"]; exists { | ||
302 | r.ForEach = attr.Expr | ||
303 | // We currently parse this, but don't yet do anything with it. | ||
304 | diags = append(diags, &hcl.Diagnostic{ | ||
305 | Severity: hcl.DiagError, | ||
306 | Summary: "Reserved argument name in module block", | ||
307 | Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), | ||
308 | Subject: &attr.NameRange, | ||
309 | }) | ||
310 | } | ||
311 | |||
312 | if attr, exists := content.Attributes["provider"]; exists { | ||
313 | var providerDiags hcl.Diagnostics | ||
314 | r.ProviderConfigRef, providerDiags = decodeProviderConfigRef(attr.Expr, "provider") | ||
315 | diags = append(diags, providerDiags...) | ||
316 | } | ||
317 | |||
318 | if attr, exists := content.Attributes["depends_on"]; exists { | ||
319 | deps, depsDiags := decodeDependsOn(attr) | ||
320 | diags = append(diags, depsDiags...) | ||
321 | r.DependsOn = append(r.DependsOn, deps...) | ||
322 | } | ||
323 | |||
324 | for _, block := range content.Blocks { | ||
325 | // All of the block types we accept are just reserved for future use, but some get a specialized error message. | ||
326 | switch block.Type { | ||
327 | case "lifecycle": | ||
328 | diags = append(diags, &hcl.Diagnostic{ | ||
329 | Severity: hcl.DiagError, | ||
330 | Summary: "Unsupported lifecycle block", | ||
331 | Detail: "Data resources do not have lifecycle settings, so a lifecycle block is not allowed.", | ||
332 | Subject: &block.DefRange, | ||
333 | }) | ||
334 | default: | ||
335 | diags = append(diags, &hcl.Diagnostic{ | ||
336 | Severity: hcl.DiagError, | ||
337 | Summary: "Reserved block type name in data block", | ||
338 | Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type), | ||
339 | Subject: &block.TypeRange, | ||
340 | }) | ||
341 | } | ||
342 | } | ||
343 | |||
344 | return r, diags | ||
345 | } | ||
346 | |||
347 | type ProviderConfigRef struct { | ||
348 | Name string | ||
349 | NameRange hcl.Range | ||
350 | Alias string | ||
351 | AliasRange *hcl.Range // nil if alias not set | ||
352 | } | ||
353 | |||
354 | func decodeProviderConfigRef(expr hcl.Expression, argName string) (*ProviderConfigRef, hcl.Diagnostics) { | ||
355 | var diags hcl.Diagnostics | ||
356 | |||
357 | var shimDiags hcl.Diagnostics | ||
358 | expr, shimDiags = shimTraversalInString(expr, false) | ||
359 | diags = append(diags, shimDiags...) | ||
360 | |||
361 | traversal, travDiags := hcl.AbsTraversalForExpr(expr) | ||
362 | |||
363 | // AbsTraversalForExpr produces only generic errors, so we'll discard | ||
364 | // the errors given and produce our own with extra context. If we didn't | ||
365 | // get any errors then we might still have warnings, though. | ||
366 | if !travDiags.HasErrors() { | ||
367 | diags = append(diags, travDiags...) | ||
368 | } | ||
369 | |||
370 | if len(traversal) < 1 || len(traversal) > 2 { | ||
371 | // A provider reference was given as a string literal in the legacy | ||
372 | // configuration language and there are lots of examples out there | ||
373 | // showing that usage, so we'll sniff for that situation here and | ||
374 | // produce a specialized error message for it to help users find | ||
375 | // the new correct form. | ||
376 | if exprIsNativeQuotedString(expr) { | ||
377 | diags = append(diags, &hcl.Diagnostic{ | ||
378 | Severity: hcl.DiagError, | ||
379 | Summary: "Invalid provider configuration reference", | ||
380 | Detail: "A provider configuration reference must not be given in quotes.", | ||
381 | Subject: expr.Range().Ptr(), | ||
382 | }) | ||
383 | return nil, diags | ||
384 | } | ||
385 | |||
386 | diags = append(diags, &hcl.Diagnostic{ | ||
387 | Severity: hcl.DiagError, | ||
388 | Summary: "Invalid provider configuration reference", | ||
389 | Detail: fmt.Sprintf("The %s argument requires a provider type name, optionally followed by a period and then a configuration alias.", argName), | ||
390 | Subject: expr.Range().Ptr(), | ||
391 | }) | ||
392 | return nil, diags | ||
393 | } | ||
394 | |||
395 | ret := &ProviderConfigRef{ | ||
396 | Name: traversal.RootName(), | ||
397 | NameRange: traversal[0].SourceRange(), | ||
398 | } | ||
399 | |||
400 | if len(traversal) > 1 { | ||
401 | aliasStep, ok := traversal[1].(hcl.TraverseAttr) | ||
402 | if !ok { | ||
403 | diags = append(diags, &hcl.Diagnostic{ | ||
404 | Severity: hcl.DiagError, | ||
405 | Summary: "Invalid provider configuration reference", | ||
406 | Detail: "Provider name must either stand alone or be followed by a period and then a configuration alias.", | ||
407 | Subject: traversal[1].SourceRange().Ptr(), | ||
408 | }) | ||
409 | return ret, diags | ||
410 | } | ||
411 | |||
412 | ret.Alias = aliasStep.Name | ||
413 | ret.AliasRange = aliasStep.SourceRange().Ptr() | ||
414 | } | ||
415 | |||
416 | return ret, diags | ||
417 | } | ||
418 | |||
419 | // Addr returns the provider config address corresponding to the receiving | ||
420 | // config reference. | ||
421 | // | ||
422 | // This is a trivial conversion, essentially just discarding the source | ||
423 | // location information and keeping just the addressing information. | ||
424 | func (r *ProviderConfigRef) Addr() addrs.ProviderConfig { | ||
425 | return addrs.ProviderConfig{ | ||
426 | Type: r.Name, | ||
427 | Alias: r.Alias, | ||
428 | } | ||
429 | } | ||
430 | |||
431 | func (r *ProviderConfigRef) String() string { | ||
432 | if r == nil { | ||
433 | return "<nil>" | ||
434 | } | ||
435 | if r.Alias != "" { | ||
436 | return fmt.Sprintf("%s.%s", r.Name, r.Alias) | ||
437 | } | ||
438 | return r.Name | ||
439 | } | ||
440 | |||
441 | var commonResourceAttributes = []hcl.AttributeSchema{ | ||
442 | { | ||
443 | Name: "count", | ||
444 | }, | ||
445 | { | ||
446 | Name: "for_each", | ||
447 | }, | ||
448 | { | ||
449 | Name: "provider", | ||
450 | }, | ||
451 | { | ||
452 | Name: "depends_on", | ||
453 | }, | ||
454 | } | ||
455 | |||
456 | var resourceBlockSchema = &hcl.BodySchema{ | ||
457 | Attributes: commonResourceAttributes, | ||
458 | Blocks: []hcl.BlockHeaderSchema{ | ||
459 | {Type: "locals"}, // reserved for future use | ||
460 | {Type: "lifecycle"}, | ||
461 | {Type: "connection"}, | ||
462 | {Type: "provisioner", LabelNames: []string{"type"}}, | ||
463 | }, | ||
464 | } | ||
465 | |||
466 | var dataBlockSchema = &hcl.BodySchema{ | ||
467 | Attributes: commonResourceAttributes, | ||
468 | Blocks: []hcl.BlockHeaderSchema{ | ||
469 | {Type: "lifecycle"}, // reserved for future use | ||
470 | {Type: "locals"}, // reserved for future use | ||
471 | }, | ||
472 | } | ||
473 | |||
474 | var resourceLifecycleBlockSchema = &hcl.BodySchema{ | ||
475 | Attributes: []hcl.AttributeSchema{ | ||
476 | { | ||
477 | Name: "create_before_destroy", | ||
478 | }, | ||
479 | { | ||
480 | Name: "prevent_destroy", | ||
481 | }, | ||
482 | { | ||
483 | Name: "ignore_changes", | ||
484 | }, | ||
485 | }, | ||
486 | } | ||