diff options
author | Alex Pilon <apilon@hashicorp.com> | 2019-02-22 18:24:37 -0500 |
---|---|---|
committer | Alex Pilon <apilon@hashicorp.com> | 2019-02-22 18:24:37 -0500 |
commit | 15c0b25d011f37e7c20aeca9eaf461f78285b8d9 (patch) | |
tree | 255c250a5c9d4801c74092d33b7337d8c14438ff /vendor/github.com/hashicorp/terraform/config | |
parent | 07971ca38143c5faf951d152fba370ddcbe26ad5 (diff) | |
download | terraform-provider-statuscake-15c0b25d011f37e7c20aeca9eaf461f78285b8d9.tar.gz terraform-provider-statuscake-15c0b25d011f37e7c20aeca9eaf461f78285b8d9.tar.zst terraform-provider-statuscake-15c0b25d011f37e7c20aeca9eaf461f78285b8d9.zip |
deps: github.com/hashicorp/terraform@sdk-v0.11-with-go-modules
Updated via: go get github.com/hashicorp/terraform@sdk-v0.11-with-go-modules and go mod tidy
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/config')
31 files changed, 3022 insertions, 330 deletions
diff --git a/vendor/github.com/hashicorp/terraform/config/append.go b/vendor/github.com/hashicorp/terraform/config/append.go index 5f4e89e..9d80c42 100644 --- a/vendor/github.com/hashicorp/terraform/config/append.go +++ b/vendor/github.com/hashicorp/terraform/config/append.go | |||
@@ -82,5 +82,11 @@ func Append(c1, c2 *Config) (*Config, error) { | |||
82 | c.Variables = append(c.Variables, c2.Variables...) | 82 | c.Variables = append(c.Variables, c2.Variables...) |
83 | } | 83 | } |
84 | 84 | ||
85 | if len(c1.Locals) > 0 || len(c2.Locals) > 0 { | ||
86 | c.Locals = make([]*Local, 0, len(c1.Locals)+len(c2.Locals)) | ||
87 | c.Locals = append(c.Locals, c1.Locals...) | ||
88 | c.Locals = append(c.Locals, c2.Locals...) | ||
89 | } | ||
90 | |||
85 | return c, nil | 91 | return c, nil |
86 | } | 92 | } |
diff --git a/vendor/github.com/hashicorp/terraform/config/config.go b/vendor/github.com/hashicorp/terraform/config/config.go index 3f756dc..1772fd7 100644 --- a/vendor/github.com/hashicorp/terraform/config/config.go +++ b/vendor/github.com/hashicorp/terraform/config/config.go | |||
@@ -8,11 +8,11 @@ import ( | |||
8 | "strconv" | 8 | "strconv" |
9 | "strings" | 9 | "strings" |
10 | 10 | ||
11 | "github.com/hashicorp/go-multierror" | 11 | hcl2 "github.com/hashicorp/hcl2/hcl" |
12 | "github.com/hashicorp/hil" | ||
13 | "github.com/hashicorp/hil/ast" | 12 | "github.com/hashicorp/hil/ast" |
14 | "github.com/hashicorp/terraform/helper/hilmapstructure" | 13 | "github.com/hashicorp/terraform/helper/hilmapstructure" |
15 | "github.com/hashicorp/terraform/plugin/discovery" | 14 | "github.com/hashicorp/terraform/plugin/discovery" |
15 | "github.com/hashicorp/terraform/tfdiags" | ||
16 | "github.com/mitchellh/reflectwalk" | 16 | "github.com/mitchellh/reflectwalk" |
17 | ) | 17 | ) |
18 | 18 | ||
@@ -34,6 +34,7 @@ type Config struct { | |||
34 | ProviderConfigs []*ProviderConfig | 34 | ProviderConfigs []*ProviderConfig |
35 | Resources []*Resource | 35 | Resources []*Resource |
36 | Variables []*Variable | 36 | Variables []*Variable |
37 | Locals []*Local | ||
37 | Outputs []*Output | 38 | Outputs []*Output |
38 | 39 | ||
39 | // The fields below can be filled in by loaders for validation | 40 | // The fields below can be filled in by loaders for validation |
@@ -55,6 +56,8 @@ type AtlasConfig struct { | |||
55 | type Module struct { | 56 | type Module struct { |
56 | Name string | 57 | Name string |
57 | Source string | 58 | Source string |
59 | Version string | ||
60 | Providers map[string]string | ||
58 | RawConfig *RawConfig | 61 | RawConfig *RawConfig |
59 | } | 62 | } |
60 | 63 | ||
@@ -147,7 +150,7 @@ func (p *Provisioner) Copy() *Provisioner { | |||
147 | } | 150 | } |
148 | } | 151 | } |
149 | 152 | ||
150 | // Variable is a variable defined within the configuration. | 153 | // Variable is a module argument defined within the configuration. |
151 | type Variable struct { | 154 | type Variable struct { |
152 | Name string | 155 | Name string |
153 | DeclaredType string `mapstructure:"type"` | 156 | DeclaredType string `mapstructure:"type"` |
@@ -155,6 +158,12 @@ type Variable struct { | |||
155 | Description string | 158 | Description string |
156 | } | 159 | } |
157 | 160 | ||
161 | // Local is a local value defined within the configuration. | ||
162 | type Local struct { | ||
163 | Name string | ||
164 | RawConfig *RawConfig | ||
165 | } | ||
166 | |||
158 | // Output is an output defined within the configuration. An output is | 167 | // Output is an output defined within the configuration. An output is |
159 | // resulting data that is highlighted by Terraform when finished. An | 168 | // resulting data that is highlighted by Terraform when finished. An |
160 | // output marked Sensitive will be output in a masked form following | 169 | // output marked Sensitive will be output in a masked form following |
@@ -222,7 +231,10 @@ func (r *Resource) Count() (int, error) { | |||
222 | 231 | ||
223 | v, err := strconv.ParseInt(count, 0, 0) | 232 | v, err := strconv.ParseInt(count, 0, 0) |
224 | if err != nil { | 233 | if err != nil { |
225 | return 0, err | 234 | return 0, fmt.Errorf( |
235 | "cannot parse %q as an integer", | ||
236 | count, | ||
237 | ) | ||
226 | } | 238 | } |
227 | 239 | ||
228 | return int(v), nil | 240 | return int(v), nil |
@@ -253,7 +265,9 @@ func (r *Resource) ProviderFullName() string { | |||
253 | // the provider name is inferred from the resource type name. | 265 | // the provider name is inferred from the resource type name. |
254 | func ResourceProviderFullName(resourceType, explicitProvider string) string { | 266 | func ResourceProviderFullName(resourceType, explicitProvider string) string { |
255 | if explicitProvider != "" { | 267 | if explicitProvider != "" { |
256 | return explicitProvider | 268 | // check for an explicit provider name, or return the original |
269 | parts := strings.SplitAfter(explicitProvider, "provider.") | ||
270 | return parts[len(parts)-1] | ||
257 | } | 271 | } |
258 | 272 | ||
259 | idx := strings.IndexRune(resourceType, '_') | 273 | idx := strings.IndexRune(resourceType, '_') |
@@ -268,30 +282,35 @@ func ResourceProviderFullName(resourceType, explicitProvider string) string { | |||
268 | } | 282 | } |
269 | 283 | ||
270 | // Validate does some basic semantic checking of the configuration. | 284 | // Validate does some basic semantic checking of the configuration. |
271 | func (c *Config) Validate() error { | 285 | func (c *Config) Validate() tfdiags.Diagnostics { |
272 | if c == nil { | 286 | if c == nil { |
273 | return nil | 287 | return nil |
274 | } | 288 | } |
275 | 289 | ||
276 | var errs []error | 290 | var diags tfdiags.Diagnostics |
277 | 291 | ||
278 | for _, k := range c.unknownKeys { | 292 | for _, k := range c.unknownKeys { |
279 | errs = append(errs, fmt.Errorf( | 293 | diags = diags.Append( |
280 | "Unknown root level key: %s", k)) | 294 | fmt.Errorf("Unknown root level key: %s", k), |
295 | ) | ||
281 | } | 296 | } |
282 | 297 | ||
283 | // Validate the Terraform config | 298 | // Validate the Terraform config |
284 | if tf := c.Terraform; tf != nil { | 299 | if tf := c.Terraform; tf != nil { |
285 | errs = append(errs, c.Terraform.Validate()...) | 300 | errs := c.Terraform.Validate() |
301 | for _, err := range errs { | ||
302 | diags = diags.Append(err) | ||
303 | } | ||
286 | } | 304 | } |
287 | 305 | ||
288 | vars := c.InterpolatedVariables() | 306 | vars := c.InterpolatedVariables() |
289 | varMap := make(map[string]*Variable) | 307 | varMap := make(map[string]*Variable) |
290 | for _, v := range c.Variables { | 308 | for _, v := range c.Variables { |
291 | if _, ok := varMap[v.Name]; ok { | 309 | if _, ok := varMap[v.Name]; ok { |
292 | errs = append(errs, fmt.Errorf( | 310 | diags = diags.Append(fmt.Errorf( |
293 | "Variable '%s': duplicate found. Variable names must be unique.", | 311 | "Variable '%s': duplicate found. Variable names must be unique.", |
294 | v.Name)) | 312 | v.Name, |
313 | )) | ||
295 | } | 314 | } |
296 | 315 | ||
297 | varMap[v.Name] = v | 316 | varMap[v.Name] = v |
@@ -299,17 +318,19 @@ func (c *Config) Validate() error { | |||
299 | 318 | ||
300 | for k, _ := range varMap { | 319 | for k, _ := range varMap { |
301 | if !NameRegexp.MatchString(k) { | 320 | if !NameRegexp.MatchString(k) { |
302 | errs = append(errs, fmt.Errorf( | 321 | diags = diags.Append(fmt.Errorf( |
303 | "variable %q: variable name must match regular expresion %s", | 322 | "variable %q: variable name must match regular expression %s", |
304 | k, NameRegexp)) | 323 | k, NameRegexp, |
324 | )) | ||
305 | } | 325 | } |
306 | } | 326 | } |
307 | 327 | ||
308 | for _, v := range c.Variables { | 328 | for _, v := range c.Variables { |
309 | if v.Type() == VariableTypeUnknown { | 329 | if v.Type() == VariableTypeUnknown { |
310 | errs = append(errs, fmt.Errorf( | 330 | diags = diags.Append(fmt.Errorf( |
311 | "Variable '%s': must be a string or a map", | 331 | "Variable '%s': must be a string or a map", |
312 | v.Name)) | 332 | v.Name, |
333 | )) | ||
313 | continue | 334 | continue |
314 | } | 335 | } |
315 | 336 | ||
@@ -330,9 +351,10 @@ func (c *Config) Validate() error { | |||
330 | if v.Default != nil { | 351 | if v.Default != nil { |
331 | if err := reflectwalk.Walk(v.Default, w); err == nil { | 352 | if err := reflectwalk.Walk(v.Default, w); err == nil { |
332 | if interp { | 353 | if interp { |
333 | errs = append(errs, fmt.Errorf( | 354 | diags = diags.Append(fmt.Errorf( |
334 | "Variable '%s': cannot contain interpolations", | 355 | "variable %q: default may not contain interpolations", |
335 | v.Name)) | 356 | v.Name, |
357 | )) | ||
336 | } | 358 | } |
337 | } | 359 | } |
338 | } | 360 | } |
@@ -348,10 +370,11 @@ func (c *Config) Validate() error { | |||
348 | } | 370 | } |
349 | 371 | ||
350 | if _, ok := varMap[uv.Name]; !ok { | 372 | if _, ok := varMap[uv.Name]; !ok { |
351 | errs = append(errs, fmt.Errorf( | 373 | diags = diags.Append(fmt.Errorf( |
352 | "%s: unknown variable referenced: '%s'. define it with 'variable' blocks", | 374 | "%s: unknown variable referenced: '%s'; define it with a 'variable' block", |
353 | source, | 375 | source, |
354 | uv.Name)) | 376 | uv.Name, |
377 | )) | ||
355 | } | 378 | } |
356 | } | 379 | } |
357 | } | 380 | } |
@@ -362,17 +385,19 @@ func (c *Config) Validate() error { | |||
362 | switch v := rawV.(type) { | 385 | switch v := rawV.(type) { |
363 | case *CountVariable: | 386 | case *CountVariable: |
364 | if v.Type == CountValueInvalid { | 387 | if v.Type == CountValueInvalid { |
365 | errs = append(errs, fmt.Errorf( | 388 | diags = diags.Append(fmt.Errorf( |
366 | "%s: invalid count variable: %s", | 389 | "%s: invalid count variable: %s", |
367 | source, | 390 | source, |
368 | v.FullKey())) | 391 | v.FullKey(), |
392 | )) | ||
369 | } | 393 | } |
370 | case *PathVariable: | 394 | case *PathVariable: |
371 | if v.Type == PathValueInvalid { | 395 | if v.Type == PathValueInvalid { |
372 | errs = append(errs, fmt.Errorf( | 396 | diags = diags.Append(fmt.Errorf( |
373 | "%s: invalid path variable: %s", | 397 | "%s: invalid path variable: %s", |
374 | source, | 398 | source, |
375 | v.FullKey())) | 399 | v.FullKey(), |
400 | )) | ||
376 | } | 401 | } |
377 | } | 402 | } |
378 | } | 403 | } |
@@ -380,27 +405,35 @@ func (c *Config) Validate() error { | |||
380 | 405 | ||
381 | // Check that providers aren't declared multiple times and that their | 406 | // Check that providers aren't declared multiple times and that their |
382 | // version constraints, where present, are syntactically valid. | 407 | // version constraints, where present, are syntactically valid. |
383 | providerSet := make(map[string]struct{}) | 408 | providerSet := make(map[string]bool) |
384 | for _, p := range c.ProviderConfigs { | 409 | for _, p := range c.ProviderConfigs { |
385 | name := p.FullName() | 410 | name := p.FullName() |
386 | if _, ok := providerSet[name]; ok { | 411 | if _, ok := providerSet[name]; ok { |
387 | errs = append(errs, fmt.Errorf( | 412 | diags = diags.Append(fmt.Errorf( |
388 | "provider.%s: declared multiple times, you can only declare a provider once", | 413 | "provider.%s: multiple configurations present; only one configuration is allowed per provider", |
389 | name)) | 414 | name, |
415 | )) | ||
390 | continue | 416 | continue |
391 | } | 417 | } |
392 | 418 | ||
393 | if p.Version != "" { | 419 | if p.Version != "" { |
394 | _, err := discovery.ConstraintStr(p.Version).Parse() | 420 | _, err := discovery.ConstraintStr(p.Version).Parse() |
395 | if err != nil { | 421 | if err != nil { |
396 | errs = append(errs, fmt.Errorf( | 422 | diags = diags.Append(&hcl2.Diagnostic{ |
397 | "provider.%s: invalid version constraint %q: %s", | 423 | Severity: hcl2.DiagError, |
398 | name, p.Version, err, | 424 | Summary: "Invalid provider version constraint", |
399 | )) | 425 | Detail: fmt.Sprintf( |
426 | "The value %q given for provider.%s is not a valid version constraint.", | ||
427 | p.Version, name, | ||
428 | ), | ||
429 | // TODO: include a "Subject" source reference in here, | ||
430 | // once the config loader is able to retain source | ||
431 | // location information. | ||
432 | }) | ||
400 | } | 433 | } |
401 | } | 434 | } |
402 | 435 | ||
403 | providerSet[name] = struct{}{} | 436 | providerSet[name] = true |
404 | } | 437 | } |
405 | 438 | ||
406 | // Check that all references to modules are valid | 439 | // Check that all references to modules are valid |
@@ -412,9 +445,10 @@ func (c *Config) Validate() error { | |||
412 | if _, ok := dupped[m.Id()]; !ok { | 445 | if _, ok := dupped[m.Id()]; !ok { |
413 | dupped[m.Id()] = struct{}{} | 446 | dupped[m.Id()] = struct{}{} |
414 | 447 | ||
415 | errs = append(errs, fmt.Errorf( | 448 | diags = diags.Append(fmt.Errorf( |
416 | "%s: module repeated multiple times", | 449 | "module %q: module repeated multiple times", |
417 | m.Id())) | 450 | m.Id(), |
451 | )) | ||
418 | } | 452 | } |
419 | 453 | ||
420 | // Already seen this module, just skip it | 454 | // Already seen this module, just skip it |
@@ -428,21 +462,23 @@ func (c *Config) Validate() error { | |||
428 | "root": m.Source, | 462 | "root": m.Source, |
429 | }) | 463 | }) |
430 | if err != nil { | 464 | if err != nil { |
431 | errs = append(errs, fmt.Errorf( | 465 | diags = diags.Append(fmt.Errorf( |
432 | "%s: module source error: %s", | 466 | "module %q: module source error: %s", |
433 | m.Id(), err)) | 467 | m.Id(), err, |
468 | )) | ||
434 | } else if len(rc.Interpolations) > 0 { | 469 | } else if len(rc.Interpolations) > 0 { |
435 | errs = append(errs, fmt.Errorf( | 470 | diags = diags.Append(fmt.Errorf( |
436 | "%s: module source cannot contain interpolations", | 471 | "module %q: module source cannot contain interpolations", |
437 | m.Id())) | 472 | m.Id(), |
473 | )) | ||
438 | } | 474 | } |
439 | 475 | ||
440 | // Check that the name matches our regexp | 476 | // Check that the name matches our regexp |
441 | if !NameRegexp.Match([]byte(m.Name)) { | 477 | if !NameRegexp.Match([]byte(m.Name)) { |
442 | errs = append(errs, fmt.Errorf( | 478 | diags = diags.Append(fmt.Errorf( |
443 | "%s: module name can only contain letters, numbers, "+ | 479 | "module %q: module name must be a letter or underscore followed by only letters, numbers, dashes, and underscores", |
444 | "dashes, and underscores", | 480 | m.Id(), |
445 | m.Id())) | 481 | )) |
446 | } | 482 | } |
447 | 483 | ||
448 | // Check that the configuration can all be strings, lists or maps | 484 | // Check that the configuration can all be strings, lists or maps |
@@ -466,30 +502,47 @@ func (c *Config) Validate() error { | |||
466 | continue | 502 | continue |
467 | } | 503 | } |
468 | 504 | ||
469 | errs = append(errs, fmt.Errorf( | 505 | diags = diags.Append(fmt.Errorf( |
470 | "%s: variable %s must be a string, list or map value", | 506 | "module %q: argument %s must have a string, list, or map value", |
471 | m.Id(), k)) | 507 | m.Id(), k, |
508 | )) | ||
472 | } | 509 | } |
473 | 510 | ||
474 | // Check for invalid count variables | 511 | // Check for invalid count variables |
475 | for _, v := range m.RawConfig.Variables { | 512 | for _, v := range m.RawConfig.Variables { |
476 | switch v.(type) { | 513 | switch v.(type) { |
477 | case *CountVariable: | 514 | case *CountVariable: |
478 | errs = append(errs, fmt.Errorf( | 515 | diags = diags.Append(fmt.Errorf( |
479 | "%s: count variables are only valid within resources", m.Name)) | 516 | "module %q: count variables are only valid within resources", |
517 | m.Name, | ||
518 | )) | ||
480 | case *SelfVariable: | 519 | case *SelfVariable: |
481 | errs = append(errs, fmt.Errorf( | 520 | diags = diags.Append(fmt.Errorf( |
482 | "%s: self variables are only valid within resources", m.Name)) | 521 | "module %q: self variables are only valid within resources", |
522 | m.Name, | ||
523 | )) | ||
483 | } | 524 | } |
484 | } | 525 | } |
485 | 526 | ||
486 | // Update the raw configuration to only contain the string values | 527 | // Update the raw configuration to only contain the string values |
487 | m.RawConfig, err = NewRawConfig(raw) | 528 | m.RawConfig, err = NewRawConfig(raw) |
488 | if err != nil { | 529 | if err != nil { |
489 | errs = append(errs, fmt.Errorf( | 530 | diags = diags.Append(fmt.Errorf( |
490 | "%s: can't initialize configuration: %s", | 531 | "%s: can't initialize configuration: %s", |
491 | m.Id(), err)) | 532 | m.Id(), err, |
533 | )) | ||
492 | } | 534 | } |
535 | |||
536 | // check that all named providers actually exist | ||
537 | for _, p := range m.Providers { | ||
538 | if !providerSet[p] { | ||
539 | diags = diags.Append(fmt.Errorf( | ||
540 | "module %q: cannot pass non-existent provider %q", | ||
541 | m.Name, p, | ||
542 | )) | ||
543 | } | ||
544 | } | ||
545 | |||
493 | } | 546 | } |
494 | dupped = nil | 547 | dupped = nil |
495 | 548 | ||
@@ -503,10 +556,10 @@ func (c *Config) Validate() error { | |||
503 | } | 556 | } |
504 | 557 | ||
505 | if _, ok := modules[mv.Name]; !ok { | 558 | if _, ok := modules[mv.Name]; !ok { |
506 | errs = append(errs, fmt.Errorf( | 559 | diags = diags.Append(fmt.Errorf( |
507 | "%s: unknown module referenced: %s", | 560 | "%s: unknown module referenced: %s", |
508 | source, | 561 | source, mv.Name, |
509 | mv.Name)) | 562 | )) |
510 | } | 563 | } |
511 | } | 564 | } |
512 | } | 565 | } |
@@ -519,9 +572,10 @@ func (c *Config) Validate() error { | |||
519 | if _, ok := dupped[r.Id()]; !ok { | 572 | if _, ok := dupped[r.Id()]; !ok { |
520 | dupped[r.Id()] = struct{}{} | 573 | dupped[r.Id()] = struct{}{} |
521 | 574 | ||
522 | errs = append(errs, fmt.Errorf( | 575 | diags = diags.Append(fmt.Errorf( |
523 | "%s: resource repeated multiple times", | 576 | "%s: resource repeated multiple times", |
524 | r.Id())) | 577 | r.Id(), |
578 | )) | ||
525 | } | 579 | } |
526 | } | 580 | } |
527 | 581 | ||
@@ -535,53 +589,42 @@ func (c *Config) Validate() error { | |||
535 | for _, v := range r.RawCount.Variables { | 589 | for _, v := range r.RawCount.Variables { |
536 | switch v.(type) { | 590 | switch v.(type) { |
537 | case *CountVariable: | 591 | case *CountVariable: |
538 | errs = append(errs, fmt.Errorf( | 592 | diags = diags.Append(fmt.Errorf( |
539 | "%s: resource count can't reference count variable: %s", | 593 | "%s: resource count can't reference count variable: %s", |
540 | n, | 594 | n, v.FullKey(), |
541 | v.FullKey())) | 595 | )) |
542 | case *SimpleVariable: | 596 | case *SimpleVariable: |
543 | errs = append(errs, fmt.Errorf( | 597 | diags = diags.Append(fmt.Errorf( |
544 | "%s: resource count can't reference variable: %s", | 598 | "%s: resource count can't reference variable: %s", |
545 | n, | 599 | n, v.FullKey(), |
546 | v.FullKey())) | 600 | )) |
547 | 601 | ||
548 | // Good | 602 | // Good |
549 | case *ModuleVariable: | 603 | case *ModuleVariable: |
550 | case *ResourceVariable: | 604 | case *ResourceVariable: |
551 | case *TerraformVariable: | 605 | case *TerraformVariable: |
552 | case *UserVariable: | 606 | case *UserVariable: |
607 | case *LocalVariable: | ||
553 | 608 | ||
554 | default: | 609 | default: |
555 | errs = append(errs, fmt.Errorf( | 610 | diags = diags.Append(fmt.Errorf( |
556 | "Internal error. Unknown type in count var in %s: %T", | 611 | "Internal error. Unknown type in count var in %s: %T", |
557 | n, v)) | 612 | n, v, |
613 | )) | ||
558 | } | 614 | } |
559 | } | 615 | } |
560 | 616 | ||
561 | // Interpolate with a fixed number to verify that its a number. | 617 | if !r.RawCount.couldBeInteger() { |
562 | r.RawCount.interpolate(func(root ast.Node) (interface{}, error) { | 618 | diags = diags.Append(fmt.Errorf( |
563 | // Execute the node but transform the AST so that it returns | 619 | "%s: resource count must be an integer", n, |
564 | // a fixed value of "5" for all interpolations. | 620 | )) |
565 | result, err := hil.Eval( | ||
566 | hil.FixedValueTransform( | ||
567 | root, &ast.LiteralNode{Value: "5", Typex: ast.TypeString}), | ||
568 | nil) | ||
569 | if err != nil { | ||
570 | return "", err | ||
571 | } | ||
572 | |||
573 | return result.Value, nil | ||
574 | }) | ||
575 | _, err := strconv.ParseInt(r.RawCount.Value().(string), 0, 0) | ||
576 | if err != nil { | ||
577 | errs = append(errs, fmt.Errorf( | ||
578 | "%s: resource count must be an integer", | ||
579 | n)) | ||
580 | } | 621 | } |
581 | r.RawCount.init() | 622 | r.RawCount.init() |
582 | 623 | ||
583 | // Validate DependsOn | 624 | // Validate DependsOn |
584 | errs = append(errs, c.validateDependsOn(n, r.DependsOn, resources, modules)...) | 625 | for _, err := range c.validateDependsOn(n, r.DependsOn, resources, modules) { |
626 | diags = diags.Append(err) | ||
627 | } | ||
585 | 628 | ||
586 | // Verify provisioners | 629 | // Verify provisioners |
587 | for _, p := range r.Provisioners { | 630 | for _, p := range r.Provisioners { |
@@ -595,9 +638,10 @@ func (c *Config) Validate() error { | |||
595 | } | 638 | } |
596 | 639 | ||
597 | if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name { | 640 | if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name { |
598 | errs = append(errs, fmt.Errorf( | 641 | diags = diags.Append(fmt.Errorf( |
599 | "%s: connection info cannot contain splat variable "+ | 642 | "%s: connection info cannot contain splat variable referencing itself", |
600 | "referencing itself", n)) | 643 | n, |
644 | )) | ||
601 | break | 645 | break |
602 | } | 646 | } |
603 | } | 647 | } |
@@ -609,9 +653,10 @@ func (c *Config) Validate() error { | |||
609 | } | 653 | } |
610 | 654 | ||
611 | if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name { | 655 | if rv.Multi && rv.Index == -1 && rv.Type == r.Type && rv.Name == r.Name { |
612 | errs = append(errs, fmt.Errorf( | 656 | diags = diags.Append(fmt.Errorf( |
613 | "%s: connection info cannot contain splat variable "+ | 657 | "%s: connection info cannot contain splat variable referencing itself", |
614 | "referencing itself", n)) | 658 | n, |
659 | )) | ||
615 | break | 660 | break |
616 | } | 661 | } |
617 | } | 662 | } |
@@ -619,21 +664,24 @@ func (c *Config) Validate() error { | |||
619 | // Check for invalid when/onFailure values, though this should be | 664 | // Check for invalid when/onFailure values, though this should be |
620 | // picked up by the loader we check here just in case. | 665 | // picked up by the loader we check here just in case. |
621 | if p.When == ProvisionerWhenInvalid { | 666 | if p.When == ProvisionerWhenInvalid { |
622 | errs = append(errs, fmt.Errorf( | 667 | diags = diags.Append(fmt.Errorf( |
623 | "%s: provisioner 'when' value is invalid", n)) | 668 | "%s: provisioner 'when' value is invalid", n, |
669 | )) | ||
624 | } | 670 | } |
625 | if p.OnFailure == ProvisionerOnFailureInvalid { | 671 | if p.OnFailure == ProvisionerOnFailureInvalid { |
626 | errs = append(errs, fmt.Errorf( | 672 | diags = diags.Append(fmt.Errorf( |
627 | "%s: provisioner 'on_failure' value is invalid", n)) | 673 | "%s: provisioner 'on_failure' value is invalid", n, |
674 | )) | ||
628 | } | 675 | } |
629 | } | 676 | } |
630 | 677 | ||
631 | // Verify ignore_changes contains valid entries | 678 | // Verify ignore_changes contains valid entries |
632 | for _, v := range r.Lifecycle.IgnoreChanges { | 679 | for _, v := range r.Lifecycle.IgnoreChanges { |
633 | if strings.Contains(v, "*") && v != "*" { | 680 | if strings.Contains(v, "*") && v != "*" { |
634 | errs = append(errs, fmt.Errorf( | 681 | diags = diags.Append(fmt.Errorf( |
635 | "%s: ignore_changes does not support using a partial string "+ | 682 | "%s: ignore_changes does not support using a partial string together with a wildcard: %s", |
636 | "together with a wildcard: %s", n, v)) | 683 | n, v, |
684 | )) | ||
637 | } | 685 | } |
638 | } | 686 | } |
639 | 687 | ||
@@ -642,21 +690,24 @@ func (c *Config) Validate() error { | |||
642 | "root": r.Lifecycle.IgnoreChanges, | 690 | "root": r.Lifecycle.IgnoreChanges, |
643 | }) | 691 | }) |
644 | if err != nil { | 692 | if err != nil { |
645 | errs = append(errs, fmt.Errorf( | 693 | diags = diags.Append(fmt.Errorf( |
646 | "%s: lifecycle ignore_changes error: %s", | 694 | "%s: lifecycle ignore_changes error: %s", |
647 | n, err)) | 695 | n, err, |
696 | )) | ||
648 | } else if len(rc.Interpolations) > 0 { | 697 | } else if len(rc.Interpolations) > 0 { |
649 | errs = append(errs, fmt.Errorf( | 698 | diags = diags.Append(fmt.Errorf( |
650 | "%s: lifecycle ignore_changes cannot contain interpolations", | 699 | "%s: lifecycle ignore_changes cannot contain interpolations", |
651 | n)) | 700 | n, |
701 | )) | ||
652 | } | 702 | } |
653 | 703 | ||
654 | // If it is a data source then it can't have provisioners | 704 | // If it is a data source then it can't have provisioners |
655 | if r.Mode == DataResourceMode { | 705 | if r.Mode == DataResourceMode { |
656 | if _, ok := r.RawConfig.Raw["provisioner"]; ok { | 706 | if _, ok := r.RawConfig.Raw["provisioner"]; ok { |
657 | errs = append(errs, fmt.Errorf( | 707 | diags = diags.Append(fmt.Errorf( |
658 | "%s: data sources cannot have provisioners", | 708 | "%s: data sources cannot have provisioners", |
659 | n)) | 709 | n, |
710 | )) | ||
660 | } | 711 | } |
661 | } | 712 | } |
662 | } | 713 | } |
@@ -670,25 +721,50 @@ func (c *Config) Validate() error { | |||
670 | 721 | ||
671 | id := rv.ResourceId() | 722 | id := rv.ResourceId() |
672 | if _, ok := resources[id]; !ok { | 723 | if _, ok := resources[id]; !ok { |
673 | errs = append(errs, fmt.Errorf( | 724 | diags = diags.Append(fmt.Errorf( |
674 | "%s: unknown resource '%s' referenced in variable %s", | 725 | "%s: unknown resource '%s' referenced in variable %s", |
675 | source, | 726 | source, |
676 | id, | 727 | id, |
677 | rv.FullKey())) | 728 | rv.FullKey(), |
729 | )) | ||
678 | continue | 730 | continue |
679 | } | 731 | } |
680 | } | 732 | } |
681 | } | 733 | } |
682 | 734 | ||
735 | // Check that all locals are valid | ||
736 | { | ||
737 | found := make(map[string]struct{}) | ||
738 | for _, l := range c.Locals { | ||
739 | if _, ok := found[l.Name]; ok { | ||
740 | diags = diags.Append(fmt.Errorf( | ||
741 | "%s: duplicate local. local value names must be unique", | ||
742 | l.Name, | ||
743 | )) | ||
744 | continue | ||
745 | } | ||
746 | found[l.Name] = struct{}{} | ||
747 | |||
748 | for _, v := range l.RawConfig.Variables { | ||
749 | if _, ok := v.(*CountVariable); ok { | ||
750 | diags = diags.Append(fmt.Errorf( | ||
751 | "local %s: count variables are only valid within resources", l.Name, | ||
752 | )) | ||
753 | } | ||
754 | } | ||
755 | } | ||
756 | } | ||
757 | |||
683 | // Check that all outputs are valid | 758 | // Check that all outputs are valid |
684 | { | 759 | { |
685 | found := make(map[string]struct{}) | 760 | found := make(map[string]struct{}) |
686 | for _, o := range c.Outputs { | 761 | for _, o := range c.Outputs { |
687 | // Verify the output is new | 762 | // Verify the output is new |
688 | if _, ok := found[o.Name]; ok { | 763 | if _, ok := found[o.Name]; ok { |
689 | errs = append(errs, fmt.Errorf( | 764 | diags = diags.Append(fmt.Errorf( |
690 | "%s: duplicate output. output names must be unique.", | 765 | "output %q: an output of this name was already defined", |
691 | o.Name)) | 766 | o.Name, |
767 | )) | ||
692 | continue | 768 | continue |
693 | } | 769 | } |
694 | found[o.Name] = struct{}{} | 770 | found[o.Name] = struct{}{} |
@@ -708,9 +784,10 @@ func (c *Config) Validate() error { | |||
708 | continue | 784 | continue |
709 | } | 785 | } |
710 | 786 | ||
711 | errs = append(errs, fmt.Errorf( | 787 | diags = diags.Append(fmt.Errorf( |
712 | "%s: value for 'sensitive' must be boolean", | 788 | "output %q: value for 'sensitive' must be boolean", |
713 | o.Name)) | 789 | o.Name, |
790 | )) | ||
714 | continue | 791 | continue |
715 | } | 792 | } |
716 | if k == "description" { | 793 | if k == "description" { |
@@ -719,27 +796,78 @@ func (c *Config) Validate() error { | |||
719 | continue | 796 | continue |
720 | } | 797 | } |
721 | 798 | ||
722 | errs = append(errs, fmt.Errorf( | 799 | diags = diags.Append(fmt.Errorf( |
723 | "%s: value for 'description' must be string", | 800 | "output %q: value for 'description' must be string", |
724 | o.Name)) | 801 | o.Name, |
802 | )) | ||
725 | continue | 803 | continue |
726 | } | 804 | } |
727 | invalidKeys = append(invalidKeys, k) | 805 | invalidKeys = append(invalidKeys, k) |
728 | } | 806 | } |
729 | if len(invalidKeys) > 0 { | 807 | if len(invalidKeys) > 0 { |
730 | errs = append(errs, fmt.Errorf( | 808 | diags = diags.Append(fmt.Errorf( |
731 | "%s: output has invalid keys: %s", | 809 | "output %q: invalid keys: %s", |
732 | o.Name, strings.Join(invalidKeys, ", "))) | 810 | o.Name, strings.Join(invalidKeys, ", "), |
811 | )) | ||
733 | } | 812 | } |
734 | if !valueKeyFound { | 813 | if !valueKeyFound { |
735 | errs = append(errs, fmt.Errorf( | 814 | diags = diags.Append(fmt.Errorf( |
736 | "%s: output is missing required 'value' key", o.Name)) | 815 | "output %q: missing required 'value' argument", o.Name, |
816 | )) | ||
737 | } | 817 | } |
738 | 818 | ||
739 | for _, v := range o.RawConfig.Variables { | 819 | for _, v := range o.RawConfig.Variables { |
740 | if _, ok := v.(*CountVariable); ok { | 820 | if _, ok := v.(*CountVariable); ok { |
741 | errs = append(errs, fmt.Errorf( | 821 | diags = diags.Append(fmt.Errorf( |
742 | "%s: count variables are only valid within resources", o.Name)) | 822 | "output %q: count variables are only valid within resources", |
823 | o.Name, | ||
824 | )) | ||
825 | } | ||
826 | } | ||
827 | |||
828 | // Detect a common mistake of using a "count"ed resource in | ||
829 | // an output value without using the splat or index form. | ||
830 | // Prior to 0.11 this error was silently ignored, but outputs | ||
831 | // now have their errors checked like all other contexts. | ||
832 | // | ||
833 | // TODO: Remove this in 0.12. | ||
834 | for _, v := range o.RawConfig.Variables { | ||
835 | rv, ok := v.(*ResourceVariable) | ||
836 | if !ok { | ||
837 | continue | ||
838 | } | ||
839 | |||
840 | // If the variable seems to be treating the referenced | ||
841 | // resource as a singleton (no count specified) then | ||
842 | // we'll check to make sure it is indeed a singleton. | ||
843 | // It's a warning if not. | ||
844 | |||
845 | if rv.Multi || rv.Index != 0 { | ||
846 | // This reference is treating the resource as a | ||
847 | // multi-resource, so the warning doesn't apply. | ||
848 | continue | ||
849 | } | ||
850 | |||
851 | for _, r := range c.Resources { | ||
852 | if r.Id() != rv.ResourceId() { | ||
853 | continue | ||
854 | } | ||
855 | |||
856 | // We test specifically for the raw string "1" here | ||
857 | // because we _do_ want to generate this warning if | ||
858 | // the user has provided an expression that happens | ||
859 | // to return 1 right now, to catch situations where | ||
860 | // a count might dynamically be set to something | ||
861 | // other than 1 and thus splat syntax is still needed | ||
862 | // to be safe. | ||
863 | if r.RawCount != nil && r.RawCount.Raw != nil && r.RawCount.Raw["count"] != "1" && rv.Field != "count" { | ||
864 | diags = diags.Append(tfdiags.SimpleWarning(fmt.Sprintf( | ||
865 | "output %q: must use splat syntax to access %s attribute %q, because it has \"count\" set; use %s.*.%s to obtain a list of the attributes across all instances", | ||
866 | o.Name, | ||
867 | r.Id(), rv.Field, | ||
868 | r.Id(), rv.Field, | ||
869 | ))) | ||
870 | } | ||
743 | } | 871 | } |
744 | } | 872 | } |
745 | } | 873 | } |
@@ -755,17 +883,15 @@ func (c *Config) Validate() error { | |||
755 | 883 | ||
756 | for _, v := range rc.Variables { | 884 | for _, v := range rc.Variables { |
757 | if _, ok := v.(*SelfVariable); ok { | 885 | if _, ok := v.(*SelfVariable); ok { |
758 | errs = append(errs, fmt.Errorf( | 886 | diags = diags.Append(fmt.Errorf( |
759 | "%s: cannot contain self-reference %s", source, v.FullKey())) | 887 | "%s: cannot contain self-reference %s", |
888 | source, v.FullKey(), | ||
889 | )) | ||
760 | } | 890 | } |
761 | } | 891 | } |
762 | } | 892 | } |
763 | 893 | ||
764 | if len(errs) > 0 { | 894 | return diags |
765 | return &multierror.Error{Errors: errs} | ||
766 | } | ||
767 | |||
768 | return nil | ||
769 | } | 895 | } |
770 | 896 | ||
771 | // InterpolatedVariables is a helper that returns a mapping of all the interpolated | 897 | // InterpolatedVariables is a helper that returns a mapping of all the interpolated |
diff --git a/vendor/github.com/hashicorp/terraform/config/config_string.go b/vendor/github.com/hashicorp/terraform/config/config_string.go index 0b3abbc..a6933c2 100644 --- a/vendor/github.com/hashicorp/terraform/config/config_string.go +++ b/vendor/github.com/hashicorp/terraform/config/config_string.go | |||
@@ -143,6 +143,46 @@ func outputsStr(os []*Output) string { | |||
143 | result += fmt.Sprintf(" %s: %s\n", kind, str) | 143 | result += fmt.Sprintf(" %s: %s\n", kind, str) |
144 | } | 144 | } |
145 | } | 145 | } |
146 | |||
147 | if o.Description != "" { | ||
148 | result += fmt.Sprintf(" description\n %s\n", o.Description) | ||
149 | } | ||
150 | } | ||
151 | |||
152 | return strings.TrimSpace(result) | ||
153 | } | ||
154 | |||
155 | func localsStr(ls []*Local) string { | ||
156 | ns := make([]string, 0, len(ls)) | ||
157 | m := make(map[string]*Local) | ||
158 | for _, l := range ls { | ||
159 | ns = append(ns, l.Name) | ||
160 | m[l.Name] = l | ||
161 | } | ||
162 | sort.Strings(ns) | ||
163 | |||
164 | result := "" | ||
165 | for _, n := range ns { | ||
166 | l := m[n] | ||
167 | |||
168 | result += fmt.Sprintf("%s\n", n) | ||
169 | |||
170 | if len(l.RawConfig.Variables) > 0 { | ||
171 | result += fmt.Sprintf(" vars\n") | ||
172 | for _, rawV := range l.RawConfig.Variables { | ||
173 | kind := "unknown" | ||
174 | str := rawV.FullKey() | ||
175 | |||
176 | switch rawV.(type) { | ||
177 | case *ResourceVariable: | ||
178 | kind = "resource" | ||
179 | case *UserVariable: | ||
180 | kind = "user" | ||
181 | } | ||
182 | |||
183 | result += fmt.Sprintf(" %s: %s\n", kind, str) | ||
184 | } | ||
185 | } | ||
146 | } | 186 | } |
147 | 187 | ||
148 | return strings.TrimSpace(result) | 188 | return strings.TrimSpace(result) |
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go b/vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go new file mode 100644 index 0000000..2b1b0ca --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/configschema/decoder_spec.go | |||
@@ -0,0 +1,97 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/hcl2/hcldec" | ||
5 | "github.com/zclconf/go-cty/cty" | ||
6 | ) | ||
7 | |||
8 | var mapLabelNames = []string{"key"} | ||
9 | |||
10 | // DecoderSpec returns a hcldec.Spec that can be used to decode a HCL Body | ||
11 | // using the facilities in the hcldec package. | ||
12 | // | ||
13 | // The returned specification is guaranteed to return a value of the same type | ||
14 | // returned by method ImpliedType, but it may contain null or unknown values if | ||
15 | // any of the block attributes are defined as optional and/or computed | ||
16 | // respectively. | ||
17 | func (b *Block) DecoderSpec() hcldec.Spec { | ||
18 | ret := hcldec.ObjectSpec{} | ||
19 | if b == nil { | ||
20 | return ret | ||
21 | } | ||
22 | |||
23 | for name, attrS := range b.Attributes { | ||
24 | switch { | ||
25 | case attrS.Computed && attrS.Optional: | ||
26 | // In this special case we use an unknown value as a default | ||
27 | // to get the intended behavior that the result is computed | ||
28 | // unless it has been explicitly set in config. | ||
29 | ret[name] = &hcldec.DefaultSpec{ | ||
30 | Primary: &hcldec.AttrSpec{ | ||
31 | Name: name, | ||
32 | Type: attrS.Type, | ||
33 | }, | ||
34 | Default: &hcldec.LiteralSpec{ | ||
35 | Value: cty.UnknownVal(attrS.Type), | ||
36 | }, | ||
37 | } | ||
38 | case attrS.Computed: | ||
39 | ret[name] = &hcldec.LiteralSpec{ | ||
40 | Value: cty.UnknownVal(attrS.Type), | ||
41 | } | ||
42 | default: | ||
43 | ret[name] = &hcldec.AttrSpec{ | ||
44 | Name: name, | ||
45 | Type: attrS.Type, | ||
46 | Required: attrS.Required, | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | |||
51 | for name, blockS := range b.BlockTypes { | ||
52 | if _, exists := ret[name]; exists { | ||
53 | // This indicates an invalid schema, since it's not valid to | ||
54 | // define both an attribute and a block type of the same name. | ||
55 | // However, we don't raise this here since it's checked by | ||
56 | // InternalValidate. | ||
57 | continue | ||
58 | } | ||
59 | |||
60 | childSpec := blockS.Block.DecoderSpec() | ||
61 | |||
62 | switch blockS.Nesting { | ||
63 | case NestingSingle: | ||
64 | ret[name] = &hcldec.BlockSpec{ | ||
65 | TypeName: name, | ||
66 | Nested: childSpec, | ||
67 | Required: blockS.MinItems == 1 && blockS.MaxItems >= 1, | ||
68 | } | ||
69 | case NestingList: | ||
70 | ret[name] = &hcldec.BlockListSpec{ | ||
71 | TypeName: name, | ||
72 | Nested: childSpec, | ||
73 | MinItems: blockS.MinItems, | ||
74 | MaxItems: blockS.MaxItems, | ||
75 | } | ||
76 | case NestingSet: | ||
77 | ret[name] = &hcldec.BlockSetSpec{ | ||
78 | TypeName: name, | ||
79 | Nested: childSpec, | ||
80 | MinItems: blockS.MinItems, | ||
81 | MaxItems: blockS.MaxItems, | ||
82 | } | ||
83 | case NestingMap: | ||
84 | ret[name] = &hcldec.BlockMapSpec{ | ||
85 | TypeName: name, | ||
86 | Nested: childSpec, | ||
87 | LabelNames: mapLabelNames, | ||
88 | } | ||
89 | default: | ||
90 | // Invalid nesting type is just ignored. It's checked by | ||
91 | // InternalValidate. | ||
92 | continue | ||
93 | } | ||
94 | } | ||
95 | |||
96 | return ret | ||
97 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/doc.go b/vendor/github.com/hashicorp/terraform/config/configschema/doc.go new file mode 100644 index 0000000..caf8d73 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/configschema/doc.go | |||
@@ -0,0 +1,14 @@ | |||
1 | // Package configschema contains types for describing the expected structure | ||
2 | // of a configuration block whose shape is not known until runtime. | ||
3 | // | ||
4 | // For example, this is used to describe the expected contents of a resource | ||
5 | // configuration block, which is defined by the corresponding provider plugin | ||
6 | // and thus not compiled into Terraform core. | ||
7 | // | ||
8 | // A configschema primarily describes the shape of configuration, but it is | ||
9 | // also suitable for use with other structures derived from the configuration, | ||
10 | // such as the cached state of a resource or a resource diff. | ||
11 | // | ||
12 | // This package should not be confused with the package helper/schema, which | ||
13 | // is the higher-level helper library used to implement providers themselves. | ||
14 | package configschema | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go b/vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go new file mode 100644 index 0000000..67324eb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/configschema/implied_type.go | |||
@@ -0,0 +1,21 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/hcl2/hcldec" | ||
5 | "github.com/zclconf/go-cty/cty" | ||
6 | ) | ||
7 | |||
8 | // ImpliedType returns the cty.Type that would result from decoding a | ||
9 | // configuration block using the receiving block schema. | ||
10 | // | ||
11 | // ImpliedType always returns a result, even if the given schema is | ||
12 | // inconsistent. Code that creates configschema.Block objects should be | ||
13 | // tested using the InternalValidate method to detect any inconsistencies | ||
14 | // that would cause this method to fall back on defaults and assumptions. | ||
15 | func (b *Block) ImpliedType() cty.Type { | ||
16 | if b == nil { | ||
17 | return cty.EmptyObject | ||
18 | } | ||
19 | |||
20 | return hcldec.ImpliedType(b.DecoderSpec()) | ||
21 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go b/vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go new file mode 100644 index 0000000..33cbe88 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/configschema/internal_validate.go | |||
@@ -0,0 +1,92 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "regexp" | ||
6 | |||
7 | "github.com/zclconf/go-cty/cty" | ||
8 | |||
9 | multierror "github.com/hashicorp/go-multierror" | ||
10 | ) | ||
11 | |||
12 | var validName = regexp.MustCompile(`^[a-z0-9_]+$`) | ||
13 | |||
14 | // InternalValidate returns an error if the receiving block and its child | ||
15 | // schema definitions have any consistencies with the documented rules for | ||
16 | // valid schema. | ||
17 | // | ||
18 | // This is intended to be used within unit tests to detect when a given | ||
19 | // schema is invalid. | ||
20 | func (b *Block) InternalValidate() error { | ||
21 | if b == nil { | ||
22 | return fmt.Errorf("top-level block schema is nil") | ||
23 | } | ||
24 | return b.internalValidate("", nil) | ||
25 | |||
26 | } | ||
27 | |||
28 | func (b *Block) internalValidate(prefix string, err error) error { | ||
29 | for name, attrS := range b.Attributes { | ||
30 | if attrS == nil { | ||
31 | err = multierror.Append(err, fmt.Errorf("%s%s: attribute schema is nil", prefix, name)) | ||
32 | continue | ||
33 | } | ||
34 | if !validName.MatchString(name) { | ||
35 | err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) | ||
36 | } | ||
37 | if attrS.Optional == false && attrS.Required == false && attrS.Computed == false { | ||
38 | err = multierror.Append(err, fmt.Errorf("%s%s: must set Optional, Required or Computed", prefix, name)) | ||
39 | } | ||
40 | if attrS.Optional && attrS.Required { | ||
41 | err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Optional and Required", prefix, name)) | ||
42 | } | ||
43 | if attrS.Computed && attrS.Required { | ||
44 | err = multierror.Append(err, fmt.Errorf("%s%s: cannot set both Computed and Required", prefix, name)) | ||
45 | } | ||
46 | if attrS.Type == cty.NilType { | ||
47 | err = multierror.Append(err, fmt.Errorf("%s%s: Type must be set to something other than cty.NilType", prefix, name)) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | for name, blockS := range b.BlockTypes { | ||
52 | if blockS == nil { | ||
53 | err = multierror.Append(err, fmt.Errorf("%s%s: block schema is nil", prefix, name)) | ||
54 | continue | ||
55 | } | ||
56 | |||
57 | if _, isAttr := b.Attributes[name]; isAttr { | ||
58 | err = multierror.Append(err, fmt.Errorf("%s%s: name defined as both attribute and child block type", prefix, name)) | ||
59 | } else if !validName.MatchString(name) { | ||
60 | err = multierror.Append(err, fmt.Errorf("%s%s: name may contain only lowercase letters, digits and underscores", prefix, name)) | ||
61 | } | ||
62 | |||
63 | if blockS.MinItems < 0 || blockS.MaxItems < 0 { | ||
64 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name)) | ||
65 | } | ||
66 | |||
67 | switch blockS.Nesting { | ||
68 | case NestingSingle: | ||
69 | switch { | ||
70 | case blockS.MinItems != blockS.MaxItems: | ||
71 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must match in NestingSingle mode", prefix, name)) | ||
72 | case blockS.MinItems < 0 || blockS.MinItems > 1: | ||
73 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must be set to either 0 or 1 in NestingSingle mode", prefix, name)) | ||
74 | } | ||
75 | case NestingList, NestingSet: | ||
76 | if blockS.MinItems > blockS.MaxItems && blockS.MaxItems != 0 { | ||
77 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems must be less than or equal to MaxItems in %s mode", prefix, name, blockS.Nesting)) | ||
78 | } | ||
79 | case NestingMap: | ||
80 | if blockS.MinItems != 0 || blockS.MaxItems != 0 { | ||
81 | err = multierror.Append(err, fmt.Errorf("%s%s: MinItems and MaxItems must both be 0 in NestingMap mode", prefix, name)) | ||
82 | } | ||
83 | default: | ||
84 | err = multierror.Append(err, fmt.Errorf("%s%s: invalid nesting mode %s", prefix, name, blockS.Nesting)) | ||
85 | } | ||
86 | |||
87 | subPrefix := prefix + name + "." | ||
88 | err = blockS.Block.internalValidate(subPrefix, err) | ||
89 | } | ||
90 | |||
91 | return err | ||
92 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go b/vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go new file mode 100644 index 0000000..6cb9313 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/configschema/nestingmode_string.go | |||
@@ -0,0 +1,16 @@ | |||
1 | // Code generated by "stringer -type=NestingMode"; DO NOT EDIT. | ||
2 | |||
3 | package configschema | ||
4 | |||
5 | import "strconv" | ||
6 | |||
7 | const _NestingMode_name = "nestingModeInvalidNestingSingleNestingListNestingSetNestingMap" | ||
8 | |||
9 | var _NestingMode_index = [...]uint8{0, 18, 31, 42, 52, 62} | ||
10 | |||
11 | func (i NestingMode) String() string { | ||
12 | if i < 0 || i >= NestingMode(len(_NestingMode_index)-1) { | ||
13 | return "NestingMode(" + strconv.FormatInt(int64(i), 10) + ")" | ||
14 | } | ||
15 | return _NestingMode_name[_NestingMode_index[i]:_NestingMode_index[i+1]] | ||
16 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/configschema/schema.go b/vendor/github.com/hashicorp/terraform/config/configschema/schema.go new file mode 100644 index 0000000..9a8ee55 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/configschema/schema.go | |||
@@ -0,0 +1,107 @@ | |||
1 | package configschema | ||
2 | |||
3 | import ( | ||
4 | "github.com/zclconf/go-cty/cty" | ||
5 | ) | ||
6 | |||
7 | // Block represents a configuration block. | ||
8 | // | ||
9 | // "Block" here is a logical grouping construct, though it happens to map | ||
10 | // directly onto the physical block syntax of Terraform's native configuration | ||
11 | // syntax. It may be a more a matter of convention in other syntaxes, such as | ||
12 | // JSON. | ||
13 | // | ||
14 | // When converted to a value, a Block always becomes an instance of an object | ||
15 | // type derived from its defined attributes and nested blocks | ||
16 | type Block struct { | ||
17 | // Attributes describes any attributes that may appear directly inside | ||
18 | // the block. | ||
19 | Attributes map[string]*Attribute | ||
20 | |||
21 | // BlockTypes describes any nested block types that may appear directly | ||
22 | // inside the block. | ||
23 | BlockTypes map[string]*NestedBlock | ||
24 | } | ||
25 | |||
26 | // Attribute represents a configuration attribute, within a block. | ||
27 | type Attribute struct { | ||
28 | // Type is a type specification that the attribute's value must conform to. | ||
29 | Type cty.Type | ||
30 | |||
31 | // Required, if set to true, specifies that an omitted or null value is | ||
32 | // not permitted. | ||
33 | Required bool | ||
34 | |||
35 | // Optional, if set to true, specifies that an omitted or null value is | ||
36 | // permitted. This field conflicts with Required. | ||
37 | Optional bool | ||
38 | |||
39 | // Computed, if set to true, specifies that the value comes from the | ||
40 | // provider rather than from configuration. If combined with Optional, | ||
41 | // then the config may optionally provide an overridden value. | ||
42 | Computed bool | ||
43 | |||
44 | // Sensitive, if set to true, indicates that an attribute may contain | ||
45 | // sensitive information. | ||
46 | // | ||
47 | // At present nothing is done with this information, but callers are | ||
48 | // encouraged to set it where appropriate so that it may be used in the | ||
49 | // future to help Terraform mask sensitive information. (Terraform | ||
50 | // currently achieves this in a limited sense via other mechanisms.) | ||
51 | Sensitive bool | ||
52 | } | ||
53 | |||
54 | // NestedBlock represents the embedding of one block within another. | ||
55 | type NestedBlock struct { | ||
56 | // Block is the description of the block that's nested. | ||
57 | Block | ||
58 | |||
59 | // Nesting provides the nesting mode for the child block, which determines | ||
60 | // how many instances of the block are allowed, how many labels it expects, | ||
61 | // and how the resulting data will be converted into a data structure. | ||
62 | Nesting NestingMode | ||
63 | |||
64 | // MinItems and MaxItems set, for the NestingList and NestingSet nesting | ||
65 | // modes, lower and upper limits on the number of child blocks allowed | ||
66 | // of the given type. If both are left at zero, no limit is applied. | ||
67 | // | ||
68 | // As a special case, both values can be set to 1 for NestingSingle in | ||
69 | // order to indicate that a particular single block is required. | ||
70 | // | ||
71 | // These fields are ignored for other nesting modes and must both be left | ||
72 | // at zero. | ||
73 | MinItems, MaxItems int | ||
74 | } | ||
75 | |||
76 | // NestingMode is an enumeration of modes for nesting blocks inside other | ||
77 | // blocks. | ||
78 | type NestingMode int | ||
79 | |||
80 | //go:generate stringer -type=NestingMode | ||
81 | |||
82 | const ( | ||
83 | nestingModeInvalid NestingMode = iota | ||
84 | |||
85 | // NestingSingle indicates that only a single instance of a given | ||
86 | // block type is permitted, with no labels, and its content should be | ||
87 | // provided directly as an object value. | ||
88 | NestingSingle | ||
89 | |||
90 | // NestingList indicates that multiple blocks of the given type are | ||
91 | // permitted, with no labels, and that their corresponding objects should | ||
92 | // be provided in a list. | ||
93 | NestingList | ||
94 | |||
95 | // NestingSet indicates that multiple blocks of the given type are | ||
96 | // permitted, with no labels, and that their corresponding objects should | ||
97 | // be provided in a set. | ||
98 | NestingSet | ||
99 | |||
100 | // NestingMap indicates that multiple blocks of the given type are | ||
101 | // permitted, each with a single label, and that their corresponding | ||
102 | // objects should be provided in a map whose keys are the labels. | ||
103 | // | ||
104 | // It's an error, therefore, to use the same label value on multiple | ||
105 | // blocks. | ||
106 | NestingMap | ||
107 | ) | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2_shim_util.go b/vendor/github.com/hashicorp/terraform/config/hcl2_shim_util.go new file mode 100644 index 0000000..207d105 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/hcl2_shim_util.go | |||
@@ -0,0 +1,134 @@ | |||
1 | package config | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/zclconf/go-cty/cty/function/stdlib" | ||
7 | |||
8 | "github.com/hashicorp/hil/ast" | ||
9 | "github.com/hashicorp/terraform/config/hcl2shim" | ||
10 | |||
11 | hcl2 "github.com/hashicorp/hcl2/hcl" | ||
12 | "github.com/zclconf/go-cty/cty" | ||
13 | "github.com/zclconf/go-cty/cty/convert" | ||
14 | "github.com/zclconf/go-cty/cty/function" | ||
15 | ) | ||
16 | |||
17 | // --------------------------------------------------------------------------- | ||
18 | // This file contains some helper functions that are used to shim between | ||
19 | // HCL2 concepts and HCL/HIL concepts, to help us mostly preserve the existing | ||
20 | // public API that was built around HCL/HIL-oriented approaches. | ||
21 | // --------------------------------------------------------------------------- | ||
22 | |||
23 | func hcl2InterpolationFuncs() map[string]function.Function { | ||
24 | hcl2Funcs := map[string]function.Function{} | ||
25 | |||
26 | for name, hilFunc := range Funcs() { | ||
27 | hcl2Funcs[name] = hcl2InterpolationFuncShim(hilFunc) | ||
28 | } | ||
29 | |||
30 | // Some functions in the old world are dealt with inside langEvalConfig | ||
31 | // due to their legacy reliance on direct access to the symbol table. | ||
32 | // Since 0.7 they don't actually need it anymore and just ignore it, | ||
33 | // so we're cheating a bit here and exploiting that detail by passing nil. | ||
34 | hcl2Funcs["lookup"] = hcl2InterpolationFuncShim(interpolationFuncLookup(nil)) | ||
35 | hcl2Funcs["keys"] = hcl2InterpolationFuncShim(interpolationFuncKeys(nil)) | ||
36 | hcl2Funcs["values"] = hcl2InterpolationFuncShim(interpolationFuncValues(nil)) | ||
37 | |||
38 | // As a bonus, we'll provide the JSON-handling functions from the cty | ||
39 | // function library since its "jsonencode" is more complete (doesn't force | ||
40 | // weird type conversions) and HIL's type system can't represent | ||
41 | // "jsondecode" at all. The result of jsondecode will eventually be forced | ||
42 | // to conform to the HIL type system on exit into the rest of Terraform due | ||
43 | // to our shimming right now, but it should be usable for decoding _within_ | ||
44 | // an expression. | ||
45 | hcl2Funcs["jsonencode"] = stdlib.JSONEncodeFunc | ||
46 | hcl2Funcs["jsondecode"] = stdlib.JSONDecodeFunc | ||
47 | |||
48 | return hcl2Funcs | ||
49 | } | ||
50 | |||
51 | func hcl2InterpolationFuncShim(hilFunc ast.Function) function.Function { | ||
52 | spec := &function.Spec{} | ||
53 | |||
54 | for i, hilArgType := range hilFunc.ArgTypes { | ||
55 | spec.Params = append(spec.Params, function.Parameter{ | ||
56 | Type: hcl2shim.HCL2TypeForHILType(hilArgType), | ||
57 | Name: fmt.Sprintf("arg%d", i+1), // HIL args don't have names, so we'll fudge it | ||
58 | }) | ||
59 | } | ||
60 | |||
61 | if hilFunc.Variadic { | ||
62 | spec.VarParam = &function.Parameter{ | ||
63 | Type: hcl2shim.HCL2TypeForHILType(hilFunc.VariadicType), | ||
64 | Name: "varargs", // HIL args don't have names, so we'll fudge it | ||
65 | } | ||
66 | } | ||
67 | |||
68 | spec.Type = func(args []cty.Value) (cty.Type, error) { | ||
69 | return hcl2shim.HCL2TypeForHILType(hilFunc.ReturnType), nil | ||
70 | } | ||
71 | spec.Impl = func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||
72 | hilArgs := make([]interface{}, len(args)) | ||
73 | for i, arg := range args { | ||
74 | hilV := hcl2shim.HILVariableFromHCL2Value(arg) | ||
75 | |||
76 | // Although the cty function system does automatic type conversions | ||
77 | // to match the argument types, cty doesn't distinguish int and | ||
78 | // float and so we may need to adjust here to ensure that the | ||
79 | // wrapped function gets exactly the Go type it was expecting. | ||
80 | var wantType ast.Type | ||
81 | if i < len(hilFunc.ArgTypes) { | ||
82 | wantType = hilFunc.ArgTypes[i] | ||
83 | } else { | ||
84 | wantType = hilFunc.VariadicType | ||
85 | } | ||
86 | switch { | ||
87 | case hilV.Type == ast.TypeInt && wantType == ast.TypeFloat: | ||
88 | hilV.Type = wantType | ||
89 | hilV.Value = float64(hilV.Value.(int)) | ||
90 | case hilV.Type == ast.TypeFloat && wantType == ast.TypeInt: | ||
91 | hilV.Type = wantType | ||
92 | hilV.Value = int(hilV.Value.(float64)) | ||
93 | } | ||
94 | |||
95 | // HIL functions actually expect to have the outermost variable | ||
96 | // "peeled" but any nested values (in lists or maps) will | ||
97 | // still have their ast.Variable wrapping. | ||
98 | hilArgs[i] = hilV.Value | ||
99 | } | ||
100 | |||
101 | hilResult, err := hilFunc.Callback(hilArgs) | ||
102 | if err != nil { | ||
103 | return cty.DynamicVal, err | ||
104 | } | ||
105 | |||
106 | // Just as on the way in, we get back a partially-peeled ast.Variable | ||
107 | // which we need to re-wrap in order to convert it back into what | ||
108 | // we're calling a "config value". | ||
109 | rv := hcl2shim.HCL2ValueFromHILVariable(ast.Variable{ | ||
110 | Type: hilFunc.ReturnType, | ||
111 | Value: hilResult, | ||
112 | }) | ||
113 | |||
114 | return convert.Convert(rv, retType) // if result is unknown we'll force the correct type here | ||
115 | } | ||
116 | return function.New(spec) | ||
117 | } | ||
118 | |||
119 | func hcl2EvalWithUnknownVars(expr hcl2.Expression) (cty.Value, hcl2.Diagnostics) { | ||
120 | trs := expr.Variables() | ||
121 | vars := map[string]cty.Value{} | ||
122 | val := cty.DynamicVal | ||
123 | |||
124 | for _, tr := range trs { | ||
125 | name := tr.RootName() | ||
126 | vars[name] = val | ||
127 | } | ||
128 | |||
129 | ctx := &hcl2.EvalContext{ | ||
130 | Variables: vars, | ||
131 | Functions: hcl2InterpolationFuncs(), | ||
132 | } | ||
133 | return expr.Value(ctx) | ||
134 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2shim/single_attr_body.go b/vendor/github.com/hashicorp/terraform/config/hcl2shim/single_attr_body.go new file mode 100644 index 0000000..19651c8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/hcl2shim/single_attr_body.go | |||
@@ -0,0 +1,85 @@ | |||
1 | package hcl2shim | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | hcl2 "github.com/hashicorp/hcl2/hcl" | ||
7 | ) | ||
8 | |||
9 | // SingleAttrBody is a weird implementation of hcl2.Body that acts as if | ||
10 | // it has a single attribute whose value is the given expression. | ||
11 | // | ||
12 | // This is used to shim Resource.RawCount and Output.RawConfig to behave | ||
13 | // more like they do in the old HCL loader. | ||
14 | type SingleAttrBody struct { | ||
15 | Name string | ||
16 | Expr hcl2.Expression | ||
17 | } | ||
18 | |||
19 | var _ hcl2.Body = SingleAttrBody{} | ||
20 | |||
21 | func (b SingleAttrBody) Content(schema *hcl2.BodySchema) (*hcl2.BodyContent, hcl2.Diagnostics) { | ||
22 | content, all, diags := b.content(schema) | ||
23 | if !all { | ||
24 | // This should never happen because this body implementation should only | ||
25 | // be used by code that is aware that it's using a single-attr body. | ||
26 | diags = append(diags, &hcl2.Diagnostic{ | ||
27 | Severity: hcl2.DiagError, | ||
28 | Summary: "Invalid attribute", | ||
29 | Detail: fmt.Sprintf("The correct attribute name is %q.", b.Name), | ||
30 | Subject: b.Expr.Range().Ptr(), | ||
31 | }) | ||
32 | } | ||
33 | return content, diags | ||
34 | } | ||
35 | |||
36 | func (b SingleAttrBody) PartialContent(schema *hcl2.BodySchema) (*hcl2.BodyContent, hcl2.Body, hcl2.Diagnostics) { | ||
37 | content, all, diags := b.content(schema) | ||
38 | var remain hcl2.Body | ||
39 | if all { | ||
40 | // If the request matched the one attribute we represent, then the | ||
41 | // remaining body is empty. | ||
42 | remain = hcl2.EmptyBody() | ||
43 | } else { | ||
44 | remain = b | ||
45 | } | ||
46 | return content, remain, diags | ||
47 | } | ||
48 | |||
49 | func (b SingleAttrBody) content(schema *hcl2.BodySchema) (*hcl2.BodyContent, bool, hcl2.Diagnostics) { | ||
50 | ret := &hcl2.BodyContent{} | ||
51 | all := false | ||
52 | var diags hcl2.Diagnostics | ||
53 | |||
54 | for _, attrS := range schema.Attributes { | ||
55 | if attrS.Name == b.Name { | ||
56 | attrs, _ := b.JustAttributes() | ||
57 | ret.Attributes = attrs | ||
58 | all = true | ||
59 | } else if attrS.Required { | ||
60 | diags = append(diags, &hcl2.Diagnostic{ | ||
61 | Severity: hcl2.DiagError, | ||
62 | Summary: "Missing attribute", | ||
63 | Detail: fmt.Sprintf("The attribute %q is required.", attrS.Name), | ||
64 | Subject: b.Expr.Range().Ptr(), | ||
65 | }) | ||
66 | } | ||
67 | } | ||
68 | |||
69 | return ret, all, diags | ||
70 | } | ||
71 | |||
72 | func (b SingleAttrBody) JustAttributes() (hcl2.Attributes, hcl2.Diagnostics) { | ||
73 | return hcl2.Attributes{ | ||
74 | b.Name: { | ||
75 | Expr: b.Expr, | ||
76 | Name: b.Name, | ||
77 | NameRange: b.Expr.Range(), | ||
78 | Range: b.Expr.Range(), | ||
79 | }, | ||
80 | }, nil | ||
81 | } | ||
82 | |||
83 | func (b SingleAttrBody) MissingItemRange() hcl2.Range { | ||
84 | return b.Expr.Range() | ||
85 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go b/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go new file mode 100644 index 0000000..0b697a5 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/hcl2shim/values.go | |||
@@ -0,0 +1,246 @@ | |||
1 | package hcl2shim | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "math/big" | ||
6 | |||
7 | "github.com/hashicorp/hil/ast" | ||
8 | "github.com/zclconf/go-cty/cty" | ||
9 | ) | ||
10 | |||
11 | // UnknownVariableValue is a sentinel value that can be used | ||
12 | // to denote that the value of a variable is unknown at this time. | ||
13 | // RawConfig uses this information to build up data about | ||
14 | // unknown keys. | ||
15 | const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66" | ||
16 | |||
17 | // ConfigValueFromHCL2 converts a value from HCL2 (really, from the cty dynamic | ||
18 | // types library that HCL2 uses) to a value type that matches what would've | ||
19 | // been produced from the HCL-based interpolator for an equivalent structure. | ||
20 | // | ||
21 | // This function will transform a cty null value into a Go nil value, which | ||
22 | // isn't a possible outcome of the HCL/HIL-based decoder and so callers may | ||
23 | // need to detect and reject any null values. | ||
24 | func ConfigValueFromHCL2(v cty.Value) interface{} { | ||
25 | if !v.IsKnown() { | ||
26 | return UnknownVariableValue | ||
27 | } | ||
28 | if v.IsNull() { | ||
29 | return nil | ||
30 | } | ||
31 | |||
32 | switch v.Type() { | ||
33 | case cty.Bool: | ||
34 | return v.True() // like HCL.BOOL | ||
35 | case cty.String: | ||
36 | return v.AsString() // like HCL token.STRING or token.HEREDOC | ||
37 | case cty.Number: | ||
38 | // We can't match HCL _exactly_ here because it distinguishes between | ||
39 | // int and float values, but we'll get as close as we can by using | ||
40 | // an int if the number is exactly representable, and a float if not. | ||
41 | // The conversion to float will force precision to that of a float64, | ||
42 | // which is potentially losing information from the specific number | ||
43 | // given, but no worse than what HCL would've done in its own conversion | ||
44 | // to float. | ||
45 | |||
46 | f := v.AsBigFloat() | ||
47 | if i, acc := f.Int64(); acc == big.Exact { | ||
48 | // if we're on a 32-bit system and the number is too big for 32-bit | ||
49 | // int then we'll fall through here and use a float64. | ||
50 | const MaxInt = int(^uint(0) >> 1) | ||
51 | const MinInt = -MaxInt - 1 | ||
52 | if i <= int64(MaxInt) && i >= int64(MinInt) { | ||
53 | return int(i) // Like HCL token.NUMBER | ||
54 | } | ||
55 | } | ||
56 | |||
57 | f64, _ := f.Float64() | ||
58 | return f64 // like HCL token.FLOAT | ||
59 | } | ||
60 | |||
61 | if v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType() { | ||
62 | l := make([]interface{}, 0, v.LengthInt()) | ||
63 | it := v.ElementIterator() | ||
64 | for it.Next() { | ||
65 | _, ev := it.Element() | ||
66 | l = append(l, ConfigValueFromHCL2(ev)) | ||
67 | } | ||
68 | return l | ||
69 | } | ||
70 | |||
71 | if v.Type().IsMapType() || v.Type().IsObjectType() { | ||
72 | l := make(map[string]interface{}) | ||
73 | it := v.ElementIterator() | ||
74 | for it.Next() { | ||
75 | ek, ev := it.Element() | ||
76 | l[ek.AsString()] = ConfigValueFromHCL2(ev) | ||
77 | } | ||
78 | return l | ||
79 | } | ||
80 | |||
81 | // If we fall out here then we have some weird type that we haven't | ||
82 | // accounted for. This should never happen unless the caller is using | ||
83 | // capsule types, and we don't currently have any such types defined. | ||
84 | panic(fmt.Errorf("can't convert %#v to config value", v)) | ||
85 | } | ||
86 | |||
87 | // HCL2ValueFromConfigValue is the opposite of configValueFromHCL2: it takes | ||
88 | // a value as would be returned from the old interpolator and turns it into | ||
89 | // a cty.Value so it can be used within, for example, an HCL2 EvalContext. | ||
90 | func HCL2ValueFromConfigValue(v interface{}) cty.Value { | ||
91 | if v == nil { | ||
92 | return cty.NullVal(cty.DynamicPseudoType) | ||
93 | } | ||
94 | if v == UnknownVariableValue { | ||
95 | return cty.DynamicVal | ||
96 | } | ||
97 | |||
98 | switch tv := v.(type) { | ||
99 | case bool: | ||
100 | return cty.BoolVal(tv) | ||
101 | case string: | ||
102 | return cty.StringVal(tv) | ||
103 | case int: | ||
104 | return cty.NumberIntVal(int64(tv)) | ||
105 | case float64: | ||
106 | return cty.NumberFloatVal(tv) | ||
107 | case []interface{}: | ||
108 | vals := make([]cty.Value, len(tv)) | ||
109 | for i, ev := range tv { | ||
110 | vals[i] = HCL2ValueFromConfigValue(ev) | ||
111 | } | ||
112 | return cty.TupleVal(vals) | ||
113 | case map[string]interface{}: | ||
114 | vals := map[string]cty.Value{} | ||
115 | for k, ev := range tv { | ||
116 | vals[k] = HCL2ValueFromConfigValue(ev) | ||
117 | } | ||
118 | return cty.ObjectVal(vals) | ||
119 | default: | ||
120 | // HCL/HIL should never generate anything that isn't caught by | ||
121 | // the above, so if we get here something has gone very wrong. | ||
122 | panic(fmt.Errorf("can't convert %#v to cty.Value", v)) | ||
123 | } | ||
124 | } | ||
125 | |||
126 | func HILVariableFromHCL2Value(v cty.Value) ast.Variable { | ||
127 | if v.IsNull() { | ||
128 | // Caller should guarantee/check this before calling | ||
129 | panic("Null values cannot be represented in HIL") | ||
130 | } | ||
131 | if !v.IsKnown() { | ||
132 | return ast.Variable{ | ||
133 | Type: ast.TypeUnknown, | ||
134 | Value: UnknownVariableValue, | ||
135 | } | ||
136 | } | ||
137 | |||
138 | switch v.Type() { | ||
139 | case cty.Bool: | ||
140 | return ast.Variable{ | ||
141 | Type: ast.TypeBool, | ||
142 | Value: v.True(), | ||
143 | } | ||
144 | case cty.Number: | ||
145 | v := ConfigValueFromHCL2(v) | ||
146 | switch tv := v.(type) { | ||
147 | case int: | ||
148 | return ast.Variable{ | ||
149 | Type: ast.TypeInt, | ||
150 | Value: tv, | ||
151 | } | ||
152 | case float64: | ||
153 | return ast.Variable{ | ||
154 | Type: ast.TypeFloat, | ||
155 | Value: tv, | ||
156 | } | ||
157 | default: | ||
158 | // should never happen | ||
159 | panic("invalid return value for configValueFromHCL2") | ||
160 | } | ||
161 | case cty.String: | ||
162 | return ast.Variable{ | ||
163 | Type: ast.TypeString, | ||
164 | Value: v.AsString(), | ||
165 | } | ||
166 | } | ||
167 | |||
168 | if v.Type().IsListType() || v.Type().IsSetType() || v.Type().IsTupleType() { | ||
169 | l := make([]ast.Variable, 0, v.LengthInt()) | ||
170 | it := v.ElementIterator() | ||
171 | for it.Next() { | ||
172 | _, ev := it.Element() | ||
173 | l = append(l, HILVariableFromHCL2Value(ev)) | ||
174 | } | ||
175 | // If we were given a tuple then this could actually produce an invalid | ||
176 | // list with non-homogenous types, which we expect to be caught inside | ||
177 | // HIL just like a user-supplied non-homogenous list would be. | ||
178 | return ast.Variable{ | ||
179 | Type: ast.TypeList, | ||
180 | Value: l, | ||
181 | } | ||
182 | } | ||
183 | |||
184 | if v.Type().IsMapType() || v.Type().IsObjectType() { | ||
185 | l := make(map[string]ast.Variable) | ||
186 | it := v.ElementIterator() | ||
187 | for it.Next() { | ||
188 | ek, ev := it.Element() | ||
189 | l[ek.AsString()] = HILVariableFromHCL2Value(ev) | ||
190 | } | ||
191 | // If we were given an object then this could actually produce an invalid | ||
192 | // map with non-homogenous types, which we expect to be caught inside | ||
193 | // HIL just like a user-supplied non-homogenous map would be. | ||
194 | return ast.Variable{ | ||
195 | Type: ast.TypeMap, | ||
196 | Value: l, | ||
197 | } | ||
198 | } | ||
199 | |||
200 | // If we fall out here then we have some weird type that we haven't | ||
201 | // accounted for. This should never happen unless the caller is using | ||
202 | // capsule types, and we don't currently have any such types defined. | ||
203 | panic(fmt.Errorf("can't convert %#v to HIL variable", v)) | ||
204 | } | ||
205 | |||
206 | func HCL2ValueFromHILVariable(v ast.Variable) cty.Value { | ||
207 | switch v.Type { | ||
208 | case ast.TypeList: | ||
209 | vals := make([]cty.Value, len(v.Value.([]ast.Variable))) | ||
210 | for i, ev := range v.Value.([]ast.Variable) { | ||
211 | vals[i] = HCL2ValueFromHILVariable(ev) | ||
212 | } | ||
213 | return cty.TupleVal(vals) | ||
214 | case ast.TypeMap: | ||
215 | vals := make(map[string]cty.Value, len(v.Value.(map[string]ast.Variable))) | ||
216 | for k, ev := range v.Value.(map[string]ast.Variable) { | ||
217 | vals[k] = HCL2ValueFromHILVariable(ev) | ||
218 | } | ||
219 | return cty.ObjectVal(vals) | ||
220 | default: | ||
221 | return HCL2ValueFromConfigValue(v.Value) | ||
222 | } | ||
223 | } | ||
224 | |||
225 | func HCL2TypeForHILType(hilType ast.Type) cty.Type { | ||
226 | switch hilType { | ||
227 | case ast.TypeAny: | ||
228 | return cty.DynamicPseudoType | ||
229 | case ast.TypeUnknown: | ||
230 | return cty.DynamicPseudoType | ||
231 | case ast.TypeBool: | ||
232 | return cty.Bool | ||
233 | case ast.TypeInt: | ||
234 | return cty.Number | ||
235 | case ast.TypeFloat: | ||
236 | return cty.Number | ||
237 | case ast.TypeString: | ||
238 | return cty.String | ||
239 | case ast.TypeList: | ||
240 | return cty.List(cty.DynamicPseudoType) | ||
241 | case ast.TypeMap: | ||
242 | return cty.Map(cty.DynamicPseudoType) | ||
243 | default: | ||
244 | return cty.NilType // equilvalent to ast.TypeInvalid | ||
245 | } | ||
246 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/import_tree.go b/vendor/github.com/hashicorp/terraform/config/import_tree.go index 37ec11a..08cbc77 100644 --- a/vendor/github.com/hashicorp/terraform/config/import_tree.go +++ b/vendor/github.com/hashicorp/terraform/config/import_tree.go | |||
@@ -1,8 +1,12 @@ | |||
1 | package config | 1 | package config |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "bufio" | ||
4 | "fmt" | 5 | "fmt" |
5 | "io" | 6 | "io" |
7 | "os" | ||
8 | |||
9 | "github.com/hashicorp/errwrap" | ||
6 | ) | 10 | ) |
7 | 11 | ||
8 | // configurable is an interface that must be implemented by any configuration | 12 | // configurable is an interface that must be implemented by any configuration |
@@ -27,15 +31,52 @@ type importTree struct { | |||
27 | // imports. | 31 | // imports. |
28 | type fileLoaderFunc func(path string) (configurable, []string, error) | 32 | type fileLoaderFunc func(path string) (configurable, []string, error) |
29 | 33 | ||
34 | // Set this to a non-empty value at link time to enable the HCL2 experiment. | ||
35 | // This is not currently enabled for release builds. | ||
36 | // | ||
37 | // For example: | ||
38 | // go install -ldflags="-X github.com/hashicorp/terraform/config.enableHCL2Experiment=true" github.com/hashicorp/terraform | ||
39 | var enableHCL2Experiment = "" | ||
40 | |||
30 | // loadTree takes a single file and loads the entire importTree for that | 41 | // loadTree takes a single file and loads the entire importTree for that |
31 | // file. This function detects what kind of configuration file it is an | 42 | // file. This function detects what kind of configuration file it is an |
32 | // executes the proper fileLoaderFunc. | 43 | // executes the proper fileLoaderFunc. |
33 | func loadTree(root string) (*importTree, error) { | 44 | func loadTree(root string) (*importTree, error) { |
34 | var f fileLoaderFunc | 45 | var f fileLoaderFunc |
35 | switch ext(root) { | 46 | |
36 | case ".tf", ".tf.json": | 47 | // HCL2 experiment is currently activated at build time via the linker. |
37 | f = loadFileHcl | 48 | // See the comment on this variable for more information. |
38 | default: | 49 | if enableHCL2Experiment == "" { |
50 | // Main-line behavior: always use the original HCL parser | ||
51 | switch ext(root) { | ||
52 | case ".tf", ".tf.json": | ||
53 | f = loadFileHcl | ||
54 | default: | ||
55 | } | ||
56 | } else { | ||
57 | // Experimental behavior: use the HCL2 parser if the opt-in comment | ||
58 | // is present. | ||
59 | switch ext(root) { | ||
60 | case ".tf": | ||
61 | // We need to sniff the file for the opt-in comment line to decide | ||
62 | // if the file is participating in the HCL2 experiment. | ||
63 | cf, err := os.Open(root) | ||
64 | if err != nil { | ||
65 | return nil, err | ||
66 | } | ||
67 | sc := bufio.NewScanner(cf) | ||
68 | for sc.Scan() { | ||
69 | if sc.Text() == "#terraform:hcl2" { | ||
70 | f = globalHCL2Loader.loadFile | ||
71 | } | ||
72 | } | ||
73 | if f == nil { | ||
74 | f = loadFileHcl | ||
75 | } | ||
76 | case ".tf.json": | ||
77 | f = loadFileHcl | ||
78 | default: | ||
79 | } | ||
39 | } | 80 | } |
40 | 81 | ||
41 | if f == nil { | 82 | if f == nil { |
@@ -86,10 +127,7 @@ func (t *importTree) Close() error { | |||
86 | func (t *importTree) ConfigTree() (*configTree, error) { | 127 | func (t *importTree) ConfigTree() (*configTree, error) { |
87 | config, err := t.Raw.Config() | 128 | config, err := t.Raw.Config() |
88 | if err != nil { | 129 | if err != nil { |
89 | return nil, fmt.Errorf( | 130 | return nil, errwrap.Wrapf(fmt.Sprintf("Error loading %s: {{err}}", t.Path), err) |
90 | "Error loading %s: %s", | ||
91 | t.Path, | ||
92 | err) | ||
93 | } | 131 | } |
94 | 132 | ||
95 | // Build our result | 133 | // Build our result |
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate.go b/vendor/github.com/hashicorp/terraform/config/interpolate.go index bbb3555..599e5ec 100644 --- a/vendor/github.com/hashicorp/terraform/config/interpolate.go +++ b/vendor/github.com/hashicorp/terraform/config/interpolate.go | |||
@@ -5,6 +5,8 @@ import ( | |||
5 | "strconv" | 5 | "strconv" |
6 | "strings" | 6 | "strings" |
7 | 7 | ||
8 | "github.com/hashicorp/terraform/tfdiags" | ||
9 | |||
8 | "github.com/hashicorp/hil/ast" | 10 | "github.com/hashicorp/hil/ast" |
9 | ) | 11 | ) |
10 | 12 | ||
@@ -14,6 +16,21 @@ import ( | |||
14 | // variables can come from: user variables, resources, etc. | 16 | // variables can come from: user variables, resources, etc. |
15 | type InterpolatedVariable interface { | 17 | type InterpolatedVariable interface { |
16 | FullKey() string | 18 | FullKey() string |
19 | SourceRange() tfdiags.SourceRange | ||
20 | } | ||
21 | |||
22 | // varRange can be embedded into an InterpolatedVariable implementation to | ||
23 | // implement the SourceRange method. | ||
24 | type varRange struct { | ||
25 | rng tfdiags.SourceRange | ||
26 | } | ||
27 | |||
28 | func (r varRange) SourceRange() tfdiags.SourceRange { | ||
29 | return r.rng | ||
30 | } | ||
31 | |||
32 | func makeVarRange(rng tfdiags.SourceRange) varRange { | ||
33 | return varRange{rng} | ||
17 | } | 34 | } |
18 | 35 | ||
19 | // CountVariable is a variable for referencing information about | 36 | // CountVariable is a variable for referencing information about |
@@ -21,6 +38,7 @@ type InterpolatedVariable interface { | |||
21 | type CountVariable struct { | 38 | type CountVariable struct { |
22 | Type CountValueType | 39 | Type CountValueType |
23 | key string | 40 | key string |
41 | varRange | ||
24 | } | 42 | } |
25 | 43 | ||
26 | // CountValueType is the type of the count variable that is referenced. | 44 | // CountValueType is the type of the count variable that is referenced. |
@@ -37,6 +55,7 @@ type ModuleVariable struct { | |||
37 | Name string | 55 | Name string |
38 | Field string | 56 | Field string |
39 | key string | 57 | key string |
58 | varRange | ||
40 | } | 59 | } |
41 | 60 | ||
42 | // A PathVariable is a variable that references path information about the | 61 | // A PathVariable is a variable that references path information about the |
@@ -44,6 +63,7 @@ type ModuleVariable struct { | |||
44 | type PathVariable struct { | 63 | type PathVariable struct { |
45 | Type PathValueType | 64 | Type PathValueType |
46 | key string | 65 | key string |
66 | varRange | ||
47 | } | 67 | } |
48 | 68 | ||
49 | type PathValueType byte | 69 | type PathValueType byte |
@@ -67,6 +87,7 @@ type ResourceVariable struct { | |||
67 | Index int // Index for multi-variable: aws_instance.foo.1.id == 1 | 87 | Index int // Index for multi-variable: aws_instance.foo.1.id == 1 |
68 | 88 | ||
69 | key string | 89 | key string |
90 | varRange | ||
70 | } | 91 | } |
71 | 92 | ||
72 | // SelfVariable is a variable that is referencing the same resource | 93 | // SelfVariable is a variable that is referencing the same resource |
@@ -75,6 +96,7 @@ type SelfVariable struct { | |||
75 | Field string | 96 | Field string |
76 | 97 | ||
77 | key string | 98 | key string |
99 | varRange | ||
78 | } | 100 | } |
79 | 101 | ||
80 | // SimpleVariable is an unprefixed variable, which can show up when users have | 102 | // SimpleVariable is an unprefixed variable, which can show up when users have |
@@ -82,6 +104,7 @@ type SelfVariable struct { | |||
82 | // internally. The template_file resource is an example of this. | 104 | // internally. The template_file resource is an example of this. |
83 | type SimpleVariable struct { | 105 | type SimpleVariable struct { |
84 | Key string | 106 | Key string |
107 | varRange | ||
85 | } | 108 | } |
86 | 109 | ||
87 | // TerraformVariable is a "terraform."-prefixed variable used to access | 110 | // TerraformVariable is a "terraform."-prefixed variable used to access |
@@ -89,6 +112,7 @@ type SimpleVariable struct { | |||
89 | type TerraformVariable struct { | 112 | type TerraformVariable struct { |
90 | Field string | 113 | Field string |
91 | key string | 114 | key string |
115 | varRange | ||
92 | } | 116 | } |
93 | 117 | ||
94 | // A UserVariable is a variable that is referencing a user variable | 118 | // A UserVariable is a variable that is referencing a user variable |
@@ -99,6 +123,14 @@ type UserVariable struct { | |||
99 | Elem string | 123 | Elem string |
100 | 124 | ||
101 | key string | 125 | key string |
126 | varRange | ||
127 | } | ||
128 | |||
129 | // A LocalVariable is a variable that references a local value defined within | ||
130 | // the current module, via a "locals" block. This looks like "${local.foo}". | ||
131 | type LocalVariable struct { | ||
132 | Name string | ||
133 | varRange | ||
102 | } | 134 | } |
103 | 135 | ||
104 | func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { | 136 | func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { |
@@ -112,6 +144,8 @@ func NewInterpolatedVariable(v string) (InterpolatedVariable, error) { | |||
112 | return NewTerraformVariable(v) | 144 | return NewTerraformVariable(v) |
113 | } else if strings.HasPrefix(v, "var.") { | 145 | } else if strings.HasPrefix(v, "var.") { |
114 | return NewUserVariable(v) | 146 | return NewUserVariable(v) |
147 | } else if strings.HasPrefix(v, "local.") { | ||
148 | return NewLocalVariable(v) | ||
115 | } else if strings.HasPrefix(v, "module.") { | 149 | } else if strings.HasPrefix(v, "module.") { |
116 | return NewModuleVariable(v) | 150 | return NewModuleVariable(v) |
117 | } else if !strings.ContainsRune(v, '.') { | 151 | } else if !strings.ContainsRune(v, '.') { |
@@ -276,7 +310,7 @@ func (v *SelfVariable) GoString() string { | |||
276 | } | 310 | } |
277 | 311 | ||
278 | func NewSimpleVariable(key string) (*SimpleVariable, error) { | 312 | func NewSimpleVariable(key string) (*SimpleVariable, error) { |
279 | return &SimpleVariable{key}, nil | 313 | return &SimpleVariable{Key: key}, nil |
280 | } | 314 | } |
281 | 315 | ||
282 | func (v *SimpleVariable) FullKey() string { | 316 | func (v *SimpleVariable) FullKey() string { |
@@ -331,6 +365,25 @@ func (v *UserVariable) GoString() string { | |||
331 | return fmt.Sprintf("*%#v", *v) | 365 | return fmt.Sprintf("*%#v", *v) |
332 | } | 366 | } |
333 | 367 | ||
368 | func NewLocalVariable(key string) (*LocalVariable, error) { | ||
369 | name := key[len("local."):] | ||
370 | if idx := strings.Index(name, "."); idx > -1 { | ||
371 | return nil, fmt.Errorf("Can't use dot (.) attribute access in local.%s; use square bracket indexing", name) | ||
372 | } | ||
373 | |||
374 | return &LocalVariable{ | ||
375 | Name: name, | ||
376 | }, nil | ||
377 | } | ||
378 | |||
379 | func (v *LocalVariable) FullKey() string { | ||
380 | return fmt.Sprintf("local.%s", v.Name) | ||
381 | } | ||
382 | |||
383 | func (v *LocalVariable) GoString() string { | ||
384 | return fmt.Sprintf("*%#v", *v) | ||
385 | } | ||
386 | |||
334 | // DetectVariables takes an AST root and returns all the interpolated | 387 | // DetectVariables takes an AST root and returns all the interpolated |
335 | // variables that are detected in the AST tree. | 388 | // variables that are detected in the AST tree. |
336 | func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) { | 389 | func DetectVariables(root ast.Node) ([]InterpolatedVariable, error) { |
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go index a298cf2..421edb0 100644 --- a/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go +++ b/vendor/github.com/hashicorp/terraform/config/interpolate_funcs.go | |||
@@ -1,17 +1,23 @@ | |||
1 | package config | 1 | package config |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "bytes" | ||
5 | "compress/gzip" | ||
4 | "crypto/md5" | 6 | "crypto/md5" |
7 | "crypto/rsa" | ||
5 | "crypto/sha1" | 8 | "crypto/sha1" |
6 | "crypto/sha256" | 9 | "crypto/sha256" |
7 | "crypto/sha512" | 10 | "crypto/sha512" |
11 | "crypto/x509" | ||
8 | "encoding/base64" | 12 | "encoding/base64" |
9 | "encoding/hex" | 13 | "encoding/hex" |
10 | "encoding/json" | 14 | "encoding/json" |
15 | "encoding/pem" | ||
11 | "fmt" | 16 | "fmt" |
12 | "io/ioutil" | 17 | "io/ioutil" |
13 | "math" | 18 | "math" |
14 | "net" | 19 | "net" |
20 | "net/url" | ||
15 | "path/filepath" | 21 | "path/filepath" |
16 | "regexp" | 22 | "regexp" |
17 | "sort" | 23 | "sort" |
@@ -55,59 +61,74 @@ func listVariableValueToStringSlice(values []ast.Variable) ([]string, error) { | |||
55 | // Funcs is the mapping of built-in functions for configuration. | 61 | // Funcs is the mapping of built-in functions for configuration. |
56 | func Funcs() map[string]ast.Function { | 62 | func Funcs() map[string]ast.Function { |
57 | return map[string]ast.Function{ | 63 | return map[string]ast.Function{ |
58 | "basename": interpolationFuncBasename(), | 64 | "abs": interpolationFuncAbs(), |
59 | "base64decode": interpolationFuncBase64Decode(), | 65 | "basename": interpolationFuncBasename(), |
60 | "base64encode": interpolationFuncBase64Encode(), | 66 | "base64decode": interpolationFuncBase64Decode(), |
61 | "base64sha256": interpolationFuncBase64Sha256(), | 67 | "base64encode": interpolationFuncBase64Encode(), |
62 | "base64sha512": interpolationFuncBase64Sha512(), | 68 | "base64gzip": interpolationFuncBase64Gzip(), |
63 | "bcrypt": interpolationFuncBcrypt(), | 69 | "base64sha256": interpolationFuncBase64Sha256(), |
64 | "ceil": interpolationFuncCeil(), | 70 | "base64sha512": interpolationFuncBase64Sha512(), |
65 | "chomp": interpolationFuncChomp(), | 71 | "bcrypt": interpolationFuncBcrypt(), |
66 | "cidrhost": interpolationFuncCidrHost(), | 72 | "ceil": interpolationFuncCeil(), |
67 | "cidrnetmask": interpolationFuncCidrNetmask(), | 73 | "chomp": interpolationFuncChomp(), |
68 | "cidrsubnet": interpolationFuncCidrSubnet(), | 74 | "cidrhost": interpolationFuncCidrHost(), |
69 | "coalesce": interpolationFuncCoalesce(), | 75 | "cidrnetmask": interpolationFuncCidrNetmask(), |
70 | "coalescelist": interpolationFuncCoalesceList(), | 76 | "cidrsubnet": interpolationFuncCidrSubnet(), |
71 | "compact": interpolationFuncCompact(), | 77 | "coalesce": interpolationFuncCoalesce(), |
72 | "concat": interpolationFuncConcat(), | 78 | "coalescelist": interpolationFuncCoalesceList(), |
73 | "contains": interpolationFuncContains(), | 79 | "compact": interpolationFuncCompact(), |
74 | "dirname": interpolationFuncDirname(), | 80 | "concat": interpolationFuncConcat(), |
75 | "distinct": interpolationFuncDistinct(), | 81 | "contains": interpolationFuncContains(), |
76 | "element": interpolationFuncElement(), | 82 | "dirname": interpolationFuncDirname(), |
77 | "file": interpolationFuncFile(), | 83 | "distinct": interpolationFuncDistinct(), |
78 | "matchkeys": interpolationFuncMatchKeys(), | 84 | "element": interpolationFuncElement(), |
79 | "floor": interpolationFuncFloor(), | 85 | "chunklist": interpolationFuncChunklist(), |
80 | "format": interpolationFuncFormat(), | 86 | "file": interpolationFuncFile(), |
81 | "formatlist": interpolationFuncFormatList(), | 87 | "filebase64sha256": interpolationFuncMakeFileHash(interpolationFuncBase64Sha256()), |
82 | "index": interpolationFuncIndex(), | 88 | "filebase64sha512": interpolationFuncMakeFileHash(interpolationFuncBase64Sha512()), |
83 | "join": interpolationFuncJoin(), | 89 | "filemd5": interpolationFuncMakeFileHash(interpolationFuncMd5()), |
84 | "jsonencode": interpolationFuncJSONEncode(), | 90 | "filesha1": interpolationFuncMakeFileHash(interpolationFuncSha1()), |
85 | "length": interpolationFuncLength(), | 91 | "filesha256": interpolationFuncMakeFileHash(interpolationFuncSha256()), |
86 | "list": interpolationFuncList(), | 92 | "filesha512": interpolationFuncMakeFileHash(interpolationFuncSha512()), |
87 | "log": interpolationFuncLog(), | 93 | "matchkeys": interpolationFuncMatchKeys(), |
88 | "lower": interpolationFuncLower(), | 94 | "flatten": interpolationFuncFlatten(), |
89 | "map": interpolationFuncMap(), | 95 | "floor": interpolationFuncFloor(), |
90 | "max": interpolationFuncMax(), | 96 | "format": interpolationFuncFormat(), |
91 | "md5": interpolationFuncMd5(), | 97 | "formatlist": interpolationFuncFormatList(), |
92 | "merge": interpolationFuncMerge(), | 98 | "indent": interpolationFuncIndent(), |
93 | "min": interpolationFuncMin(), | 99 | "index": interpolationFuncIndex(), |
94 | "pathexpand": interpolationFuncPathExpand(), | 100 | "join": interpolationFuncJoin(), |
95 | "pow": interpolationFuncPow(), | 101 | "jsonencode": interpolationFuncJSONEncode(), |
96 | "uuid": interpolationFuncUUID(), | 102 | "length": interpolationFuncLength(), |
97 | "replace": interpolationFuncReplace(), | 103 | "list": interpolationFuncList(), |
98 | "sha1": interpolationFuncSha1(), | 104 | "log": interpolationFuncLog(), |
99 | "sha256": interpolationFuncSha256(), | 105 | "lower": interpolationFuncLower(), |
100 | "sha512": interpolationFuncSha512(), | 106 | "map": interpolationFuncMap(), |
101 | "signum": interpolationFuncSignum(), | 107 | "max": interpolationFuncMax(), |
102 | "slice": interpolationFuncSlice(), | 108 | "md5": interpolationFuncMd5(), |
103 | "sort": interpolationFuncSort(), | 109 | "merge": interpolationFuncMerge(), |
104 | "split": interpolationFuncSplit(), | 110 | "min": interpolationFuncMin(), |
105 | "substr": interpolationFuncSubstr(), | 111 | "pathexpand": interpolationFuncPathExpand(), |
106 | "timestamp": interpolationFuncTimestamp(), | 112 | "pow": interpolationFuncPow(), |
107 | "title": interpolationFuncTitle(), | 113 | "uuid": interpolationFuncUUID(), |
108 | "trimspace": interpolationFuncTrimSpace(), | 114 | "replace": interpolationFuncReplace(), |
109 | "upper": interpolationFuncUpper(), | 115 | "rsadecrypt": interpolationFuncRsaDecrypt(), |
110 | "zipmap": interpolationFuncZipMap(), | 116 | "sha1": interpolationFuncSha1(), |
117 | "sha256": interpolationFuncSha256(), | ||
118 | "sha512": interpolationFuncSha512(), | ||
119 | "signum": interpolationFuncSignum(), | ||
120 | "slice": interpolationFuncSlice(), | ||
121 | "sort": interpolationFuncSort(), | ||
122 | "split": interpolationFuncSplit(), | ||
123 | "substr": interpolationFuncSubstr(), | ||
124 | "timestamp": interpolationFuncTimestamp(), | ||
125 | "timeadd": interpolationFuncTimeAdd(), | ||
126 | "title": interpolationFuncTitle(), | ||
127 | "transpose": interpolationFuncTranspose(), | ||
128 | "trimspace": interpolationFuncTrimSpace(), | ||
129 | "upper": interpolationFuncUpper(), | ||
130 | "urlencode": interpolationFuncURLEncode(), | ||
131 | "zipmap": interpolationFuncZipMap(), | ||
111 | } | 132 | } |
112 | } | 133 | } |
113 | 134 | ||
@@ -669,6 +690,21 @@ func interpolationFuncFormatList() ast.Function { | |||
669 | } | 690 | } |
670 | } | 691 | } |
671 | 692 | ||
693 | // interpolationFuncIndent indents a multi-line string with the | ||
694 | // specified number of spaces | ||
695 | func interpolationFuncIndent() ast.Function { | ||
696 | return ast.Function{ | ||
697 | ArgTypes: []ast.Type{ast.TypeInt, ast.TypeString}, | ||
698 | ReturnType: ast.TypeString, | ||
699 | Callback: func(args []interface{}) (interface{}, error) { | ||
700 | spaces := args[0].(int) | ||
701 | data := args[1].(string) | ||
702 | pad := strings.Repeat(" ", spaces) | ||
703 | return strings.Replace(data, "\n", "\n"+pad, -1), nil | ||
704 | }, | ||
705 | } | ||
706 | } | ||
707 | |||
672 | // interpolationFuncIndex implements the "index" function that allows one to | 708 | // interpolationFuncIndex implements the "index" function that allows one to |
673 | // find the index of a specific element in a list | 709 | // find the index of a specific element in a list |
674 | func interpolationFuncIndex() ast.Function { | 710 | func interpolationFuncIndex() ast.Function { |
@@ -823,8 +859,7 @@ func interpolationFuncJoin() ast.Function { | |||
823 | } | 859 | } |
824 | 860 | ||
825 | // interpolationFuncJSONEncode implements the "jsonencode" function that encodes | 861 | // interpolationFuncJSONEncode implements the "jsonencode" function that encodes |
826 | // a string, list, or map as its JSON representation. For now, values in the | 862 | // a string, list, or map as its JSON representation. |
827 | // list or map may only be strings. | ||
828 | func interpolationFuncJSONEncode() ast.Function { | 863 | func interpolationFuncJSONEncode() ast.Function { |
829 | return ast.Function{ | 864 | return ast.Function{ |
830 | ArgTypes: []ast.Type{ast.TypeAny}, | 865 | ArgTypes: []ast.Type{ast.TypeAny}, |
@@ -837,28 +872,36 @@ func interpolationFuncJSONEncode() ast.Function { | |||
837 | toEncode = typedArg | 872 | toEncode = typedArg |
838 | 873 | ||
839 | case []ast.Variable: | 874 | case []ast.Variable: |
840 | // We preallocate the list here. Note that it's important that in | ||
841 | // the length 0 case, we have an empty list rather than nil, as | ||
842 | // they encode differently. | ||
843 | // XXX It would be nice to support arbitrarily nested data here. Is | ||
844 | // there an inverse of hil.InterfaceToVariable? | ||
845 | strings := make([]string, len(typedArg)) | 875 | strings := make([]string, len(typedArg)) |
846 | 876 | ||
847 | for i, v := range typedArg { | 877 | for i, v := range typedArg { |
848 | if v.Type != ast.TypeString { | 878 | if v.Type != ast.TypeString { |
849 | return "", fmt.Errorf("list elements must be strings") | 879 | variable, _ := hil.InterfaceToVariable(typedArg) |
880 | toEncode, _ = hil.VariableToInterface(variable) | ||
881 | |||
882 | jEnc, err := json.Marshal(toEncode) | ||
883 | if err != nil { | ||
884 | return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode) | ||
885 | } | ||
886 | return string(jEnc), nil | ||
887 | |||
850 | } | 888 | } |
851 | strings[i] = v.Value.(string) | 889 | strings[i] = v.Value.(string) |
852 | } | 890 | } |
853 | toEncode = strings | 891 | toEncode = strings |
854 | 892 | ||
855 | case map[string]ast.Variable: | 893 | case map[string]ast.Variable: |
856 | // XXX It would be nice to support arbitrarily nested data here. Is | ||
857 | // there an inverse of hil.InterfaceToVariable? | ||
858 | stringMap := make(map[string]string) | 894 | stringMap := make(map[string]string) |
859 | for k, v := range typedArg { | 895 | for k, v := range typedArg { |
860 | if v.Type != ast.TypeString { | 896 | if v.Type != ast.TypeString { |
861 | return "", fmt.Errorf("map values must be strings") | 897 | variable, _ := hil.InterfaceToVariable(typedArg) |
898 | toEncode, _ = hil.VariableToInterface(variable) | ||
899 | |||
900 | jEnc, err := json.Marshal(toEncode) | ||
901 | if err != nil { | ||
902 | return "", fmt.Errorf("failed to encode JSON data '%s'", toEncode) | ||
903 | } | ||
904 | return string(jEnc), nil | ||
862 | } | 905 | } |
863 | stringMap[k] = v.Value.(string) | 906 | stringMap[k] = v.Value.(string) |
864 | } | 907 | } |
@@ -1098,6 +1141,56 @@ func interpolationFuncElement() ast.Function { | |||
1098 | } | 1141 | } |
1099 | } | 1142 | } |
1100 | 1143 | ||
1144 | // returns the `list` items chunked by `size`. | ||
1145 | func interpolationFuncChunklist() ast.Function { | ||
1146 | return ast.Function{ | ||
1147 | ArgTypes: []ast.Type{ | ||
1148 | ast.TypeList, // inputList | ||
1149 | ast.TypeInt, // size | ||
1150 | }, | ||
1151 | ReturnType: ast.TypeList, | ||
1152 | Callback: func(args []interface{}) (interface{}, error) { | ||
1153 | output := make([]ast.Variable, 0) | ||
1154 | |||
1155 | values, _ := args[0].([]ast.Variable) | ||
1156 | size, _ := args[1].(int) | ||
1157 | |||
1158 | // errors if size is negative | ||
1159 | if size < 0 { | ||
1160 | return nil, fmt.Errorf("The size argument must be positive") | ||
1161 | } | ||
1162 | |||
1163 | // if size is 0, returns a list made of the initial list | ||
1164 | if size == 0 { | ||
1165 | output = append(output, ast.Variable{ | ||
1166 | Type: ast.TypeList, | ||
1167 | Value: values, | ||
1168 | }) | ||
1169 | return output, nil | ||
1170 | } | ||
1171 | |||
1172 | variables := make([]ast.Variable, 0) | ||
1173 | chunk := ast.Variable{ | ||
1174 | Type: ast.TypeList, | ||
1175 | Value: variables, | ||
1176 | } | ||
1177 | l := len(values) | ||
1178 | for i, v := range values { | ||
1179 | variables = append(variables, v) | ||
1180 | |||
1181 | // Chunk when index isn't 0, or when reaching the values's length | ||
1182 | if (i+1)%size == 0 || (i+1) == l { | ||
1183 | chunk.Value = variables | ||
1184 | output = append(output, chunk) | ||
1185 | variables = make([]ast.Variable, 0) | ||
1186 | } | ||
1187 | } | ||
1188 | |||
1189 | return output, nil | ||
1190 | }, | ||
1191 | } | ||
1192 | } | ||
1193 | |||
1101 | // interpolationFuncKeys implements the "keys" function that yields a list of | 1194 | // interpolationFuncKeys implements the "keys" function that yields a list of |
1102 | // keys of map types within a Terraform configuration. | 1195 | // keys of map types within a Terraform configuration. |
1103 | func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function { | 1196 | func interpolationFuncKeys(vs map[string]ast.Variable) ast.Function { |
@@ -1197,6 +1290,32 @@ func interpolationFuncBase64Decode() ast.Function { | |||
1197 | } | 1290 | } |
1198 | } | 1291 | } |
1199 | 1292 | ||
1293 | // interpolationFuncBase64Gzip implements the "gzip" function that allows gzip | ||
1294 | // compression encoding the result using base64 | ||
1295 | func interpolationFuncBase64Gzip() ast.Function { | ||
1296 | return ast.Function{ | ||
1297 | ArgTypes: []ast.Type{ast.TypeString}, | ||
1298 | ReturnType: ast.TypeString, | ||
1299 | Callback: func(args []interface{}) (interface{}, error) { | ||
1300 | s := args[0].(string) | ||
1301 | |||
1302 | var b bytes.Buffer | ||
1303 | gz := gzip.NewWriter(&b) | ||
1304 | if _, err := gz.Write([]byte(s)); err != nil { | ||
1305 | return "", fmt.Errorf("failed to write gzip raw data: '%s'", s) | ||
1306 | } | ||
1307 | if err := gz.Flush(); err != nil { | ||
1308 | return "", fmt.Errorf("failed to flush gzip writer: '%s'", s) | ||
1309 | } | ||
1310 | if err := gz.Close(); err != nil { | ||
1311 | return "", fmt.Errorf("failed to close gzip writer: '%s'", s) | ||
1312 | } | ||
1313 | |||
1314 | return base64.StdEncoding.EncodeToString(b.Bytes()), nil | ||
1315 | }, | ||
1316 | } | ||
1317 | } | ||
1318 | |||
1200 | // interpolationFuncLower implements the "lower" function that does | 1319 | // interpolationFuncLower implements the "lower" function that does |
1201 | // string lower casing. | 1320 | // string lower casing. |
1202 | func interpolationFuncLower() ast.Function { | 1321 | func interpolationFuncLower() ast.Function { |
@@ -1396,6 +1515,29 @@ func interpolationFuncTimestamp() ast.Function { | |||
1396 | } | 1515 | } |
1397 | } | 1516 | } |
1398 | 1517 | ||
1518 | func interpolationFuncTimeAdd() ast.Function { | ||
1519 | return ast.Function{ | ||
1520 | ArgTypes: []ast.Type{ | ||
1521 | ast.TypeString, // input timestamp string in RFC3339 format | ||
1522 | ast.TypeString, // duration to add to input timestamp that should be parsable by time.ParseDuration | ||
1523 | }, | ||
1524 | ReturnType: ast.TypeString, | ||
1525 | Callback: func(args []interface{}) (interface{}, error) { | ||
1526 | |||
1527 | ts, err := time.Parse(time.RFC3339, args[0].(string)) | ||
1528 | if err != nil { | ||
1529 | return nil, err | ||
1530 | } | ||
1531 | duration, err := time.ParseDuration(args[1].(string)) | ||
1532 | if err != nil { | ||
1533 | return nil, err | ||
1534 | } | ||
1535 | |||
1536 | return ts.Add(duration).Format(time.RFC3339), nil | ||
1537 | }, | ||
1538 | } | ||
1539 | } | ||
1540 | |||
1399 | // interpolationFuncTitle implements the "title" function that returns a copy of the | 1541 | // interpolationFuncTitle implements the "title" function that returns a copy of the |
1400 | // string in which first characters of all the words are capitalized. | 1542 | // string in which first characters of all the words are capitalized. |
1401 | func interpolationFuncTitle() ast.Function { | 1543 | func interpolationFuncTitle() ast.Function { |
@@ -1441,7 +1583,7 @@ func interpolationFuncSubstr() ast.Function { | |||
1441 | return nil, fmt.Errorf("length should be a non-negative integer") | 1583 | return nil, fmt.Errorf("length should be a non-negative integer") |
1442 | } | 1584 | } |
1443 | 1585 | ||
1444 | if offset > len(str) { | 1586 | if offset > len(str) || offset < 0 { |
1445 | return nil, fmt.Errorf("offset cannot be larger than the length of the string") | 1587 | return nil, fmt.Errorf("offset cannot be larger than the length of the string") |
1446 | } | 1588 | } |
1447 | 1589 | ||
@@ -1453,3 +1595,160 @@ func interpolationFuncSubstr() ast.Function { | |||
1453 | }, | 1595 | }, |
1454 | } | 1596 | } |
1455 | } | 1597 | } |
1598 | |||
1599 | // Flatten until it's not ast.TypeList | ||
1600 | func flattener(finalList []ast.Variable, flattenList []ast.Variable) []ast.Variable { | ||
1601 | for _, val := range flattenList { | ||
1602 | if val.Type == ast.TypeList { | ||
1603 | finalList = flattener(finalList, val.Value.([]ast.Variable)) | ||
1604 | } else { | ||
1605 | finalList = append(finalList, val) | ||
1606 | } | ||
1607 | } | ||
1608 | return finalList | ||
1609 | } | ||
1610 | |||
1611 | // Flatten to single list | ||
1612 | func interpolationFuncFlatten() ast.Function { | ||
1613 | return ast.Function{ | ||
1614 | ArgTypes: []ast.Type{ast.TypeList}, | ||
1615 | ReturnType: ast.TypeList, | ||
1616 | Variadic: false, | ||
1617 | Callback: func(args []interface{}) (interface{}, error) { | ||
1618 | inputList := args[0].([]ast.Variable) | ||
1619 | |||
1620 | var outputList []ast.Variable | ||
1621 | return flattener(outputList, inputList), nil | ||
1622 | }, | ||
1623 | } | ||
1624 | } | ||
1625 | |||
1626 | func interpolationFuncURLEncode() ast.Function { | ||
1627 | return ast.Function{ | ||
1628 | ArgTypes: []ast.Type{ast.TypeString}, | ||
1629 | ReturnType: ast.TypeString, | ||
1630 | Callback: func(args []interface{}) (interface{}, error) { | ||
1631 | s := args[0].(string) | ||
1632 | return url.QueryEscape(s), nil | ||
1633 | }, | ||
1634 | } | ||
1635 | } | ||
1636 | |||
1637 | // interpolationFuncTranspose implements the "transpose" function | ||
1638 | // that converts a map (string,list) to a map (string,list) where | ||
1639 | // the unique values of the original lists become the keys of the | ||
1640 | // new map and the keys of the original map become values for the | ||
1641 | // corresponding new keys. | ||
1642 | func interpolationFuncTranspose() ast.Function { | ||
1643 | return ast.Function{ | ||
1644 | ArgTypes: []ast.Type{ast.TypeMap}, | ||
1645 | ReturnType: ast.TypeMap, | ||
1646 | Callback: func(args []interface{}) (interface{}, error) { | ||
1647 | |||
1648 | inputMap := args[0].(map[string]ast.Variable) | ||
1649 | outputMap := make(map[string]ast.Variable) | ||
1650 | tmpMap := make(map[string][]string) | ||
1651 | |||
1652 | for inKey, inVal := range inputMap { | ||
1653 | if inVal.Type != ast.TypeList { | ||
1654 | return nil, fmt.Errorf("transpose requires a map of lists of strings") | ||
1655 | } | ||
1656 | values := inVal.Value.([]ast.Variable) | ||
1657 | for _, listVal := range values { | ||
1658 | if listVal.Type != ast.TypeString { | ||
1659 | return nil, fmt.Errorf("transpose requires the given map values to be lists of strings") | ||
1660 | } | ||
1661 | outKey := listVal.Value.(string) | ||
1662 | if _, ok := tmpMap[outKey]; !ok { | ||
1663 | tmpMap[outKey] = make([]string, 0) | ||
1664 | } | ||
1665 | outVal := tmpMap[outKey] | ||
1666 | outVal = append(outVal, inKey) | ||
1667 | sort.Strings(outVal) | ||
1668 | tmpMap[outKey] = outVal | ||
1669 | } | ||
1670 | } | ||
1671 | |||
1672 | for outKey, outVal := range tmpMap { | ||
1673 | values := make([]ast.Variable, 0) | ||
1674 | for _, v := range outVal { | ||
1675 | values = append(values, ast.Variable{Type: ast.TypeString, Value: v}) | ||
1676 | } | ||
1677 | outputMap[outKey] = ast.Variable{Type: ast.TypeList, Value: values} | ||
1678 | } | ||
1679 | return outputMap, nil | ||
1680 | }, | ||
1681 | } | ||
1682 | } | ||
1683 | |||
1684 | // interpolationFuncAbs returns the absolute value of a given float. | ||
1685 | func interpolationFuncAbs() ast.Function { | ||
1686 | return ast.Function{ | ||
1687 | ArgTypes: []ast.Type{ast.TypeFloat}, | ||
1688 | ReturnType: ast.TypeFloat, | ||
1689 | Callback: func(args []interface{}) (interface{}, error) { | ||
1690 | return math.Abs(args[0].(float64)), nil | ||
1691 | }, | ||
1692 | } | ||
1693 | } | ||
1694 | |||
1695 | // interpolationFuncRsaDecrypt implements the "rsadecrypt" function that does | ||
1696 | // RSA decryption. | ||
1697 | func interpolationFuncRsaDecrypt() ast.Function { | ||
1698 | return ast.Function{ | ||
1699 | ArgTypes: []ast.Type{ast.TypeString, ast.TypeString}, | ||
1700 | ReturnType: ast.TypeString, | ||
1701 | Callback: func(args []interface{}) (interface{}, error) { | ||
1702 | s := args[0].(string) | ||
1703 | key := args[1].(string) | ||
1704 | |||
1705 | b, err := base64.StdEncoding.DecodeString(s) | ||
1706 | if err != nil { | ||
1707 | return "", fmt.Errorf("Failed to decode input %q: cipher text must be base64-encoded", s) | ||
1708 | } | ||
1709 | |||
1710 | block, _ := pem.Decode([]byte(key)) | ||
1711 | if block == nil { | ||
1712 | return "", fmt.Errorf("Failed to read key %q: no key found", key) | ||
1713 | } | ||
1714 | if block.Headers["Proc-Type"] == "4,ENCRYPTED" { | ||
1715 | return "", fmt.Errorf( | ||
1716 | "Failed to read key %q: password protected keys are\n"+ | ||
1717 | "not supported. Please decrypt the key prior to use.", key) | ||
1718 | } | ||
1719 | |||
1720 | x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes) | ||
1721 | if err != nil { | ||
1722 | return "", err | ||
1723 | } | ||
1724 | |||
1725 | out, err := rsa.DecryptPKCS1v15(nil, x509Key, b) | ||
1726 | if err != nil { | ||
1727 | return "", err | ||
1728 | } | ||
1729 | |||
1730 | return string(out), nil | ||
1731 | }, | ||
1732 | } | ||
1733 | } | ||
1734 | |||
1735 | // interpolationFuncMakeFileHash constructs a function that hashes the contents | ||
1736 | // of a file by combining the implementations of the file(...) function and | ||
1737 | // a given other function that is assumed to take a single string argument and | ||
1738 | // return a hash value. | ||
1739 | func interpolationFuncMakeFileHash(hashFunc ast.Function) ast.Function { | ||
1740 | fileFunc := interpolationFuncFile() | ||
1741 | |||
1742 | return ast.Function{ | ||
1743 | ArgTypes: []ast.Type{ast.TypeString}, | ||
1744 | ReturnType: ast.TypeString, | ||
1745 | Callback: func(args []interface{}) (interface{}, error) { | ||
1746 | filename := args[0].(string) | ||
1747 | contents, err := fileFunc.Callback([]interface{}{filename}) | ||
1748 | if err != nil { | ||
1749 | return nil, err | ||
1750 | } | ||
1751 | return hashFunc.Callback([]interface{}{contents}) | ||
1752 | }, | ||
1753 | } | ||
1754 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go b/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go index ead3d10..66a677d 100644 --- a/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go +++ b/vendor/github.com/hashicorp/terraform/config/interpolate_walk.go | |||
@@ -271,9 +271,7 @@ func (w *interpolationWalker) splitSlice() { | |||
271 | result = append(result, val.Value) | 271 | result = append(result, val.Value) |
272 | } | 272 | } |
273 | case []interface{}: | 273 | case []interface{}: |
274 | for _, element := range val { | 274 | result = append(result, val...) |
275 | result = append(result, element) | ||
276 | } | ||
277 | default: | 275 | default: |
278 | result = append(result, v) | 276 | result = append(result, v) |
279 | } | 277 | } |
diff --git a/vendor/github.com/hashicorp/terraform/config/loader.go b/vendor/github.com/hashicorp/terraform/config/loader.go index 5dd7d46..6e34781 100644 --- a/vendor/github.com/hashicorp/terraform/config/loader.go +++ b/vendor/github.com/hashicorp/terraform/config/loader.go | |||
@@ -80,7 +80,7 @@ func LoadDir(root string) (*Config, error) { | |||
80 | if err != nil { | 80 | if err != nil { |
81 | return nil, err | 81 | return nil, err |
82 | } | 82 | } |
83 | if len(files) == 0 { | 83 | if len(files) == 0 && len(overrides) == 0 { |
84 | return nil, &ErrNoConfigsFound{Dir: root} | 84 | return nil, &ErrNoConfigsFound{Dir: root} |
85 | } | 85 | } |
86 | 86 | ||
@@ -112,6 +112,9 @@ func LoadDir(root string) (*Config, error) { | |||
112 | result = c | 112 | result = c |
113 | } | 113 | } |
114 | } | 114 | } |
115 | if len(files) == 0 { | ||
116 | result = &Config{} | ||
117 | } | ||
115 | 118 | ||
116 | // Load all the overrides, and merge them into the config | 119 | // Load all the overrides, and merge them into the config |
117 | for _, f := range overrides { | 120 | for _, f := range overrides { |
diff --git a/vendor/github.com/hashicorp/terraform/config/loader_hcl.go b/vendor/github.com/hashicorp/terraform/config/loader_hcl.go index e85e493..68cffe2 100644 --- a/vendor/github.com/hashicorp/terraform/config/loader_hcl.go +++ b/vendor/github.com/hashicorp/terraform/config/loader_hcl.go | |||
@@ -17,10 +17,20 @@ type hclConfigurable struct { | |||
17 | Root *ast.File | 17 | Root *ast.File |
18 | } | 18 | } |
19 | 19 | ||
20 | var ReservedDataSourceFields = []string{ | ||
21 | "connection", | ||
22 | "count", | ||
23 | "depends_on", | ||
24 | "lifecycle", | ||
25 | "provider", | ||
26 | "provisioner", | ||
27 | } | ||
28 | |||
20 | var ReservedResourceFields = []string{ | 29 | var ReservedResourceFields = []string{ |
21 | "connection", | 30 | "connection", |
22 | "count", | 31 | "count", |
23 | "depends_on", | 32 | "depends_on", |
33 | "id", | ||
24 | "lifecycle", | 34 | "lifecycle", |
25 | "provider", | 35 | "provider", |
26 | "provisioner", | 36 | "provisioner", |
@@ -35,6 +45,7 @@ func (t *hclConfigurable) Config() (*Config, error) { | |||
35 | validKeys := map[string]struct{}{ | 45 | validKeys := map[string]struct{}{ |
36 | "atlas": struct{}{}, | 46 | "atlas": struct{}{}, |
37 | "data": struct{}{}, | 47 | "data": struct{}{}, |
48 | "locals": struct{}{}, | ||
38 | "module": struct{}{}, | 49 | "module": struct{}{}, |
39 | "output": struct{}{}, | 50 | "output": struct{}{}, |
40 | "provider": struct{}{}, | 51 | "provider": struct{}{}, |
@@ -70,6 +81,15 @@ func (t *hclConfigurable) Config() (*Config, error) { | |||
70 | } | 81 | } |
71 | } | 82 | } |
72 | 83 | ||
84 | // Build local values | ||
85 | if locals := list.Filter("locals"); len(locals.Items) > 0 { | ||
86 | var err error | ||
87 | config.Locals, err = loadLocalsHcl(locals) | ||
88 | if err != nil { | ||
89 | return nil, err | ||
90 | } | ||
91 | } | ||
92 | |||
73 | // Get Atlas configuration | 93 | // Get Atlas configuration |
74 | if atlas := list.Filter("atlas"); len(atlas.Items) > 0 { | 94 | if atlas := list.Filter("atlas"); len(atlas.Items) > 0 { |
75 | var err error | 95 | var err error |
@@ -373,9 +393,6 @@ func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) { | |||
373 | err) | 393 | err) |
374 | } | 394 | } |
375 | 395 | ||
376 | // Remove the fields we handle specially | ||
377 | delete(config, "source") | ||
378 | |||
379 | rawConfig, err := NewRawConfig(config) | 396 | rawConfig, err := NewRawConfig(config) |
380 | if err != nil { | 397 | if err != nil { |
381 | return nil, fmt.Errorf( | 398 | return nil, fmt.Errorf( |
@@ -384,7 +401,11 @@ func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) { | |||
384 | err) | 401 | err) |
385 | } | 402 | } |
386 | 403 | ||
387 | // If we have a count, then figure it out | 404 | // Remove the fields we handle specially |
405 | delete(config, "source") | ||
406 | delete(config, "version") | ||
407 | delete(config, "providers") | ||
408 | |||
388 | var source string | 409 | var source string |
389 | if o := listVal.Filter("source"); len(o.Items) > 0 { | 410 | if o := listVal.Filter("source"); len(o.Items) > 0 { |
390 | err = hcl.DecodeObject(&source, o.Items[0].Val) | 411 | err = hcl.DecodeObject(&source, o.Items[0].Val) |
@@ -396,9 +417,33 @@ func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) { | |||
396 | } | 417 | } |
397 | } | 418 | } |
398 | 419 | ||
420 | var version string | ||
421 | if o := listVal.Filter("version"); len(o.Items) > 0 { | ||
422 | err = hcl.DecodeObject(&version, o.Items[0].Val) | ||
423 | if err != nil { | ||
424 | return nil, fmt.Errorf( | ||
425 | "Error parsing version for %s: %s", | ||
426 | k, | ||
427 | err) | ||
428 | } | ||
429 | } | ||
430 | |||
431 | var providers map[string]string | ||
432 | if o := listVal.Filter("providers"); len(o.Items) > 0 { | ||
433 | err = hcl.DecodeObject(&providers, o.Items[0].Val) | ||
434 | if err != nil { | ||
435 | return nil, fmt.Errorf( | ||
436 | "Error parsing providers for %s: %s", | ||
437 | k, | ||
438 | err) | ||
439 | } | ||
440 | } | ||
441 | |||
399 | result = append(result, &Module{ | 442 | result = append(result, &Module{ |
400 | Name: k, | 443 | Name: k, |
401 | Source: source, | 444 | Source: source, |
445 | Version: version, | ||
446 | Providers: providers, | ||
402 | RawConfig: rawConfig, | 447 | RawConfig: rawConfig, |
403 | }) | 448 | }) |
404 | } | 449 | } |
@@ -406,6 +451,59 @@ func loadModulesHcl(list *ast.ObjectList) ([]*Module, error) { | |||
406 | return result, nil | 451 | return result, nil |
407 | } | 452 | } |
408 | 453 | ||
454 | // loadLocalsHcl recurses into the given HCL object turns it into | ||
455 | // a list of locals. | ||
456 | func loadLocalsHcl(list *ast.ObjectList) ([]*Local, error) { | ||
457 | |||
458 | result := make([]*Local, 0, len(list.Items)) | ||
459 | |||
460 | for _, block := range list.Items { | ||
461 | if len(block.Keys) > 0 { | ||
462 | return nil, fmt.Errorf( | ||
463 | "locals block at %s should not have label %q", | ||
464 | block.Pos(), block.Keys[0].Token.Value(), | ||
465 | ) | ||
466 | } | ||
467 | |||
468 | blockObj, ok := block.Val.(*ast.ObjectType) | ||
469 | if !ok { | ||
470 | return nil, fmt.Errorf("locals value at %s should be a block", block.Val.Pos()) | ||
471 | } | ||
472 | |||
473 | // blockObj now contains directly our local decls | ||
474 | for _, item := range blockObj.List.Items { | ||
475 | if len(item.Keys) != 1 { | ||
476 | return nil, fmt.Errorf("local declaration at %s may not be a block", item.Val.Pos()) | ||
477 | } | ||
478 | |||
479 | // By the time we get here there can only be one item left, but | ||
480 | // we'll decode into a map anyway because it's a convenient way | ||
481 | // to extract both the key and the value robustly. | ||
482 | kv := map[string]interface{}{} | ||
483 | hcl.DecodeObject(&kv, item) | ||
484 | for k, v := range kv { | ||
485 | rawConfig, err := NewRawConfig(map[string]interface{}{ | ||
486 | "value": v, | ||
487 | }) | ||
488 | |||
489 | if err != nil { | ||
490 | return nil, fmt.Errorf( | ||
491 | "error parsing local value %q at %s: %s", | ||
492 | k, item.Val.Pos(), err, | ||
493 | ) | ||
494 | } | ||
495 | |||
496 | result = append(result, &Local{ | ||
497 | Name: k, | ||
498 | RawConfig: rawConfig, | ||
499 | }) | ||
500 | } | ||
501 | } | ||
502 | } | ||
503 | |||
504 | return result, nil | ||
505 | } | ||
506 | |||
409 | // LoadOutputsHcl recurses into the given HCL object and turns | 507 | // LoadOutputsHcl recurses into the given HCL object and turns |
410 | // it into a mapping of outputs. | 508 | // it into a mapping of outputs. |
411 | func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) { | 509 | func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) { |
@@ -434,6 +532,7 @@ func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) { | |||
434 | 532 | ||
435 | // Delete special keys | 533 | // Delete special keys |
436 | delete(config, "depends_on") | 534 | delete(config, "depends_on") |
535 | delete(config, "description") | ||
437 | 536 | ||
438 | rawConfig, err := NewRawConfig(config) | 537 | rawConfig, err := NewRawConfig(config) |
439 | if err != nil { | 538 | if err != nil { |
@@ -455,10 +554,23 @@ func loadOutputsHcl(list *ast.ObjectList) ([]*Output, error) { | |||
455 | } | 554 | } |
456 | } | 555 | } |
457 | 556 | ||
557 | // If we have a description field, then filter that | ||
558 | var description string | ||
559 | if o := listVal.Filter("description"); len(o.Items) > 0 { | ||
560 | err := hcl.DecodeObject(&description, o.Items[0].Val) | ||
561 | if err != nil { | ||
562 | return nil, fmt.Errorf( | ||
563 | "Error reading description for output %q: %s", | ||
564 | n, | ||
565 | err) | ||
566 | } | ||
567 | } | ||
568 | |||
458 | result = append(result, &Output{ | 569 | result = append(result, &Output{ |
459 | Name: n, | 570 | Name: n, |
460 | RawConfig: rawConfig, | 571 | RawConfig: rawConfig, |
461 | DependsOn: dependsOn, | 572 | DependsOn: dependsOn, |
573 | Description: description, | ||
462 | }) | 574 | }) |
463 | } | 575 | } |
464 | 576 | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/loader_hcl2.go b/vendor/github.com/hashicorp/terraform/config/loader_hcl2.go new file mode 100644 index 0000000..4f9f129 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/loader_hcl2.go | |||
@@ -0,0 +1,473 @@ | |||
1 | package config | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "sort" | ||
6 | "strings" | ||
7 | |||
8 | gohcl2 "github.com/hashicorp/hcl2/gohcl" | ||
9 | hcl2 "github.com/hashicorp/hcl2/hcl" | ||
10 | hcl2parse "github.com/hashicorp/hcl2/hclparse" | ||
11 | "github.com/hashicorp/terraform/config/hcl2shim" | ||
12 | "github.com/zclconf/go-cty/cty" | ||
13 | ) | ||
14 | |||
15 | // hcl2Configurable is an implementation of configurable that knows | ||
16 | // how to turn a HCL Body into a *Config object. | ||
17 | type hcl2Configurable struct { | ||
18 | SourceFilename string | ||
19 | Body hcl2.Body | ||
20 | } | ||
21 | |||
22 | // hcl2Loader is a wrapper around a HCL parser that provides a fileLoaderFunc. | ||
23 | type hcl2Loader struct { | ||
24 | Parser *hcl2parse.Parser | ||
25 | } | ||
26 | |||
27 | // For the moment we'll just have a global loader since we don't have anywhere | ||
28 | // better to stash this. | ||
29 | // TODO: refactor the loader API so that it uses some sort of object we can | ||
30 | // stash the parser inside. | ||
31 | var globalHCL2Loader = newHCL2Loader() | ||
32 | |||
33 | // newHCL2Loader creates a new hcl2Loader containing a new HCL Parser. | ||
34 | // | ||
35 | // HCL parsers retain information about files that are loaded to aid in | ||
36 | // producing diagnostic messages, so all files within a single configuration | ||
37 | // should be loaded with the same parser to ensure the availability of | ||
38 | // full diagnostic information. | ||
39 | func newHCL2Loader() hcl2Loader { | ||
40 | return hcl2Loader{ | ||
41 | Parser: hcl2parse.NewParser(), | ||
42 | } | ||
43 | } | ||
44 | |||
45 | // loadFile is a fileLoaderFunc that knows how to read a HCL2 file and turn it | ||
46 | // into a hcl2Configurable. | ||
47 | func (l hcl2Loader) loadFile(filename string) (configurable, []string, error) { | ||
48 | var f *hcl2.File | ||
49 | var diags hcl2.Diagnostics | ||
50 | if strings.HasSuffix(filename, ".json") { | ||
51 | f, diags = l.Parser.ParseJSONFile(filename) | ||
52 | } else { | ||
53 | f, diags = l.Parser.ParseHCLFile(filename) | ||
54 | } | ||
55 | if diags.HasErrors() { | ||
56 | // Return diagnostics as an error; callers may type-assert this to | ||
57 | // recover the original diagnostics, if it doesn't end up wrapped | ||
58 | // in another error. | ||
59 | return nil, nil, diags | ||
60 | } | ||
61 | |||
62 | return &hcl2Configurable{ | ||
63 | SourceFilename: filename, | ||
64 | Body: f.Body, | ||
65 | }, nil, nil | ||
66 | } | ||
67 | |||
68 | func (t *hcl2Configurable) Config() (*Config, error) { | ||
69 | config := &Config{} | ||
70 | |||
71 | // these structs are used only for the initial shallow decoding; we'll | ||
72 | // expand this into the main, public-facing config structs afterwards. | ||
73 | type atlas struct { | ||
74 | Name string `hcl:"name"` | ||
75 | Include *[]string `hcl:"include"` | ||
76 | Exclude *[]string `hcl:"exclude"` | ||
77 | } | ||
78 | type provider struct { | ||
79 | Name string `hcl:"name,label"` | ||
80 | Alias *string `hcl:"alias,attr"` | ||
81 | Version *string `hcl:"version,attr"` | ||
82 | Config hcl2.Body `hcl:",remain"` | ||
83 | } | ||
84 | type module struct { | ||
85 | Name string `hcl:"name,label"` | ||
86 | Source string `hcl:"source,attr"` | ||
87 | Version *string `hcl:"version,attr"` | ||
88 | Providers *map[string]string `hcl:"providers,attr"` | ||
89 | Config hcl2.Body `hcl:",remain"` | ||
90 | } | ||
91 | type resourceLifecycle struct { | ||
92 | CreateBeforeDestroy *bool `hcl:"create_before_destroy,attr"` | ||
93 | PreventDestroy *bool `hcl:"prevent_destroy,attr"` | ||
94 | IgnoreChanges *[]string `hcl:"ignore_changes,attr"` | ||
95 | } | ||
96 | type connection struct { | ||
97 | Config hcl2.Body `hcl:",remain"` | ||
98 | } | ||
99 | type provisioner struct { | ||
100 | Type string `hcl:"type,label"` | ||
101 | |||
102 | When *string `hcl:"when,attr"` | ||
103 | OnFailure *string `hcl:"on_failure,attr"` | ||
104 | |||
105 | Connection *connection `hcl:"connection,block"` | ||
106 | Config hcl2.Body `hcl:",remain"` | ||
107 | } | ||
108 | type managedResource struct { | ||
109 | Type string `hcl:"type,label"` | ||
110 | Name string `hcl:"name,label"` | ||
111 | |||
112 | CountExpr hcl2.Expression `hcl:"count,attr"` | ||
113 | Provider *string `hcl:"provider,attr"` | ||
114 | DependsOn *[]string `hcl:"depends_on,attr"` | ||
115 | |||
116 | Lifecycle *resourceLifecycle `hcl:"lifecycle,block"` | ||
117 | Provisioners []provisioner `hcl:"provisioner,block"` | ||
118 | Connection *connection `hcl:"connection,block"` | ||
119 | |||
120 | Config hcl2.Body `hcl:",remain"` | ||
121 | } | ||
122 | type dataResource struct { | ||
123 | Type string `hcl:"type,label"` | ||
124 | Name string `hcl:"name,label"` | ||
125 | |||
126 | CountExpr hcl2.Expression `hcl:"count,attr"` | ||
127 | Provider *string `hcl:"provider,attr"` | ||
128 | DependsOn *[]string `hcl:"depends_on,attr"` | ||
129 | |||
130 | Config hcl2.Body `hcl:",remain"` | ||
131 | } | ||
132 | type variable struct { | ||
133 | Name string `hcl:"name,label"` | ||
134 | |||
135 | DeclaredType *string `hcl:"type,attr"` | ||
136 | Default *cty.Value `hcl:"default,attr"` | ||
137 | Description *string `hcl:"description,attr"` | ||
138 | Sensitive *bool `hcl:"sensitive,attr"` | ||
139 | } | ||
140 | type output struct { | ||
141 | Name string `hcl:"name,label"` | ||
142 | |||
143 | ValueExpr hcl2.Expression `hcl:"value,attr"` | ||
144 | DependsOn *[]string `hcl:"depends_on,attr"` | ||
145 | Description *string `hcl:"description,attr"` | ||
146 | Sensitive *bool `hcl:"sensitive,attr"` | ||
147 | } | ||
148 | type locals struct { | ||
149 | Definitions hcl2.Attributes `hcl:",remain"` | ||
150 | } | ||
151 | type backend struct { | ||
152 | Type string `hcl:"type,label"` | ||
153 | Config hcl2.Body `hcl:",remain"` | ||
154 | } | ||
155 | type terraform struct { | ||
156 | RequiredVersion *string `hcl:"required_version,attr"` | ||
157 | Backend *backend `hcl:"backend,block"` | ||
158 | } | ||
159 | type topLevel struct { | ||
160 | Atlas *atlas `hcl:"atlas,block"` | ||
161 | Datas []dataResource `hcl:"data,block"` | ||
162 | Modules []module `hcl:"module,block"` | ||
163 | Outputs []output `hcl:"output,block"` | ||
164 | Providers []provider `hcl:"provider,block"` | ||
165 | Resources []managedResource `hcl:"resource,block"` | ||
166 | Terraform *terraform `hcl:"terraform,block"` | ||
167 | Variables []variable `hcl:"variable,block"` | ||
168 | Locals []*locals `hcl:"locals,block"` | ||
169 | } | ||
170 | |||
171 | var raw topLevel | ||
172 | diags := gohcl2.DecodeBody(t.Body, nil, &raw) | ||
173 | if diags.HasErrors() { | ||
174 | // Do some minimal decoding to see if we can at least get the | ||
175 | // required Terraform version, which might help explain why we | ||
176 | // couldn't parse the rest. | ||
177 | if raw.Terraform != nil && raw.Terraform.RequiredVersion != nil { | ||
178 | config.Terraform = &Terraform{ | ||
179 | RequiredVersion: *raw.Terraform.RequiredVersion, | ||
180 | } | ||
181 | } | ||
182 | |||
183 | // We return the diags as an implementation of error, which the | ||
184 | // caller than then type-assert if desired to recover the individual | ||
185 | // diagnostics. | ||
186 | // FIXME: The current API gives us no way to return warnings in the | ||
187 | // absense of any errors. | ||
188 | return config, diags | ||
189 | } | ||
190 | |||
191 | if raw.Terraform != nil { | ||
192 | var reqdVersion string | ||
193 | var backend *Backend | ||
194 | |||
195 | if raw.Terraform.RequiredVersion != nil { | ||
196 | reqdVersion = *raw.Terraform.RequiredVersion | ||
197 | } | ||
198 | if raw.Terraform.Backend != nil { | ||
199 | backend = new(Backend) | ||
200 | backend.Type = raw.Terraform.Backend.Type | ||
201 | |||
202 | // We don't permit interpolations or nested blocks inside the | ||
203 | // backend config, so we can decode the config early here and | ||
204 | // get direct access to the values, which is important for the | ||
205 | // config hashing to work as expected. | ||
206 | var config map[string]string | ||
207 | configDiags := gohcl2.DecodeBody(raw.Terraform.Backend.Config, nil, &config) | ||
208 | diags = append(diags, configDiags...) | ||
209 | |||
210 | raw := make(map[string]interface{}, len(config)) | ||
211 | for k, v := range config { | ||
212 | raw[k] = v | ||
213 | } | ||
214 | |||
215 | var err error | ||
216 | backend.RawConfig, err = NewRawConfig(raw) | ||
217 | if err != nil { | ||
218 | diags = append(diags, &hcl2.Diagnostic{ | ||
219 | Severity: hcl2.DiagError, | ||
220 | Summary: "Invalid backend configuration", | ||
221 | Detail: fmt.Sprintf("Error in backend configuration: %s", err), | ||
222 | }) | ||
223 | } | ||
224 | } | ||
225 | |||
226 | config.Terraform = &Terraform{ | ||
227 | RequiredVersion: reqdVersion, | ||
228 | Backend: backend, | ||
229 | } | ||
230 | } | ||
231 | |||
232 | if raw.Atlas != nil { | ||
233 | var include, exclude []string | ||
234 | if raw.Atlas.Include != nil { | ||
235 | include = *raw.Atlas.Include | ||
236 | } | ||
237 | if raw.Atlas.Exclude != nil { | ||
238 | exclude = *raw.Atlas.Exclude | ||
239 | } | ||
240 | config.Atlas = &AtlasConfig{ | ||
241 | Name: raw.Atlas.Name, | ||
242 | Include: include, | ||
243 | Exclude: exclude, | ||
244 | } | ||
245 | } | ||
246 | |||
247 | for _, rawM := range raw.Modules { | ||
248 | m := &Module{ | ||
249 | Name: rawM.Name, | ||
250 | Source: rawM.Source, | ||
251 | RawConfig: NewRawConfigHCL2(rawM.Config), | ||
252 | } | ||
253 | |||
254 | if rawM.Version != nil { | ||
255 | m.Version = *rawM.Version | ||
256 | } | ||
257 | |||
258 | if rawM.Providers != nil { | ||
259 | m.Providers = *rawM.Providers | ||
260 | } | ||
261 | |||
262 | config.Modules = append(config.Modules, m) | ||
263 | } | ||
264 | |||
265 | for _, rawV := range raw.Variables { | ||
266 | v := &Variable{ | ||
267 | Name: rawV.Name, | ||
268 | } | ||
269 | if rawV.DeclaredType != nil { | ||
270 | v.DeclaredType = *rawV.DeclaredType | ||
271 | } | ||
272 | if rawV.Default != nil { | ||
273 | v.Default = hcl2shim.ConfigValueFromHCL2(*rawV.Default) | ||
274 | } | ||
275 | if rawV.Description != nil { | ||
276 | v.Description = *rawV.Description | ||
277 | } | ||
278 | |||
279 | config.Variables = append(config.Variables, v) | ||
280 | } | ||
281 | |||
282 | for _, rawO := range raw.Outputs { | ||
283 | o := &Output{ | ||
284 | Name: rawO.Name, | ||
285 | } | ||
286 | |||
287 | if rawO.Description != nil { | ||
288 | o.Description = *rawO.Description | ||
289 | } | ||
290 | if rawO.DependsOn != nil { | ||
291 | o.DependsOn = *rawO.DependsOn | ||
292 | } | ||
293 | if rawO.Sensitive != nil { | ||
294 | o.Sensitive = *rawO.Sensitive | ||
295 | } | ||
296 | |||
297 | // The result is expected to be a map like map[string]interface{}{"value": something}, | ||
298 | // so we'll fake that with our hcl2shim.SingleAttrBody shim. | ||
299 | o.RawConfig = NewRawConfigHCL2(hcl2shim.SingleAttrBody{ | ||
300 | Name: "value", | ||
301 | Expr: rawO.ValueExpr, | ||
302 | }) | ||
303 | |||
304 | config.Outputs = append(config.Outputs, o) | ||
305 | } | ||
306 | |||
307 | for _, rawR := range raw.Resources { | ||
308 | r := &Resource{ | ||
309 | Mode: ManagedResourceMode, | ||
310 | Type: rawR.Type, | ||
311 | Name: rawR.Name, | ||
312 | } | ||
313 | if rawR.Lifecycle != nil { | ||
314 | var l ResourceLifecycle | ||
315 | if rawR.Lifecycle.CreateBeforeDestroy != nil { | ||
316 | l.CreateBeforeDestroy = *rawR.Lifecycle.CreateBeforeDestroy | ||
317 | } | ||
318 | if rawR.Lifecycle.PreventDestroy != nil { | ||
319 | l.PreventDestroy = *rawR.Lifecycle.PreventDestroy | ||
320 | } | ||
321 | if rawR.Lifecycle.IgnoreChanges != nil { | ||
322 | l.IgnoreChanges = *rawR.Lifecycle.IgnoreChanges | ||
323 | } | ||
324 | r.Lifecycle = l | ||
325 | } | ||
326 | if rawR.Provider != nil { | ||
327 | r.Provider = *rawR.Provider | ||
328 | } | ||
329 | if rawR.DependsOn != nil { | ||
330 | r.DependsOn = *rawR.DependsOn | ||
331 | } | ||
332 | |||
333 | var defaultConnInfo *RawConfig | ||
334 | if rawR.Connection != nil { | ||
335 | defaultConnInfo = NewRawConfigHCL2(rawR.Connection.Config) | ||
336 | } | ||
337 | |||
338 | for _, rawP := range rawR.Provisioners { | ||
339 | p := &Provisioner{ | ||
340 | Type: rawP.Type, | ||
341 | } | ||
342 | |||
343 | switch { | ||
344 | case rawP.When == nil: | ||
345 | p.When = ProvisionerWhenCreate | ||
346 | case *rawP.When == "create": | ||
347 | p.When = ProvisionerWhenCreate | ||
348 | case *rawP.When == "destroy": | ||
349 | p.When = ProvisionerWhenDestroy | ||
350 | default: | ||
351 | p.When = ProvisionerWhenInvalid | ||
352 | } | ||
353 | |||
354 | switch { | ||
355 | case rawP.OnFailure == nil: | ||
356 | p.OnFailure = ProvisionerOnFailureFail | ||
357 | case *rawP.When == "fail": | ||
358 | p.OnFailure = ProvisionerOnFailureFail | ||
359 | case *rawP.When == "continue": | ||
360 | p.OnFailure = ProvisionerOnFailureContinue | ||
361 | default: | ||
362 | p.OnFailure = ProvisionerOnFailureInvalid | ||
363 | } | ||
364 | |||
365 | if rawP.Connection != nil { | ||
366 | p.ConnInfo = NewRawConfigHCL2(rawP.Connection.Config) | ||
367 | } else { | ||
368 | p.ConnInfo = defaultConnInfo | ||
369 | } | ||
370 | |||
371 | p.RawConfig = NewRawConfigHCL2(rawP.Config) | ||
372 | |||
373 | r.Provisioners = append(r.Provisioners, p) | ||
374 | } | ||
375 | |||
376 | // The old loader records the count expression as a weird RawConfig with | ||
377 | // a single-element map inside. Since the rest of the world is assuming | ||
378 | // that, we'll mimic it here. | ||
379 | { | ||
380 | countBody := hcl2shim.SingleAttrBody{ | ||
381 | Name: "count", | ||
382 | Expr: rawR.CountExpr, | ||
383 | } | ||
384 | |||
385 | r.RawCount = NewRawConfigHCL2(countBody) | ||
386 | r.RawCount.Key = "count" | ||
387 | } | ||
388 | |||
389 | r.RawConfig = NewRawConfigHCL2(rawR.Config) | ||
390 | |||
391 | config.Resources = append(config.Resources, r) | ||
392 | |||
393 | } | ||
394 | |||
395 | for _, rawR := range raw.Datas { | ||
396 | r := &Resource{ | ||
397 | Mode: DataResourceMode, | ||
398 | Type: rawR.Type, | ||
399 | Name: rawR.Name, | ||
400 | } | ||
401 | |||
402 | if rawR.Provider != nil { | ||
403 | r.Provider = *rawR.Provider | ||
404 | } | ||
405 | if rawR.DependsOn != nil { | ||
406 | r.DependsOn = *rawR.DependsOn | ||
407 | } | ||
408 | |||
409 | // The old loader records the count expression as a weird RawConfig with | ||
410 | // a single-element map inside. Since the rest of the world is assuming | ||
411 | // that, we'll mimic it here. | ||
412 | { | ||
413 | countBody := hcl2shim.SingleAttrBody{ | ||
414 | Name: "count", | ||
415 | Expr: rawR.CountExpr, | ||
416 | } | ||
417 | |||
418 | r.RawCount = NewRawConfigHCL2(countBody) | ||
419 | r.RawCount.Key = "count" | ||
420 | } | ||
421 | |||
422 | r.RawConfig = NewRawConfigHCL2(rawR.Config) | ||
423 | |||
424 | config.Resources = append(config.Resources, r) | ||
425 | } | ||
426 | |||
427 | for _, rawP := range raw.Providers { | ||
428 | p := &ProviderConfig{ | ||
429 | Name: rawP.Name, | ||
430 | } | ||
431 | |||
432 | if rawP.Alias != nil { | ||
433 | p.Alias = *rawP.Alias | ||
434 | } | ||
435 | if rawP.Version != nil { | ||
436 | p.Version = *rawP.Version | ||
437 | } | ||
438 | |||
439 | // The result is expected to be a map like map[string]interface{}{"value": something}, | ||
440 | // so we'll fake that with our hcl2shim.SingleAttrBody shim. | ||
441 | p.RawConfig = NewRawConfigHCL2(rawP.Config) | ||
442 | |||
443 | config.ProviderConfigs = append(config.ProviderConfigs, p) | ||
444 | } | ||
445 | |||
446 | for _, rawL := range raw.Locals { | ||
447 | names := make([]string, 0, len(rawL.Definitions)) | ||
448 | for n := range rawL.Definitions { | ||
449 | names = append(names, n) | ||
450 | } | ||
451 | sort.Strings(names) | ||
452 | for _, n := range names { | ||
453 | attr := rawL.Definitions[n] | ||
454 | l := &Local{ | ||
455 | Name: n, | ||
456 | RawConfig: NewRawConfigHCL2(hcl2shim.SingleAttrBody{ | ||
457 | Name: "value", | ||
458 | Expr: attr.Expr, | ||
459 | }), | ||
460 | } | ||
461 | config.Locals = append(config.Locals, l) | ||
462 | } | ||
463 | } | ||
464 | |||
465 | // FIXME: The current API gives us no way to return warnings in the | ||
466 | // absense of any errors. | ||
467 | var err error | ||
468 | if diags.HasErrors() { | ||
469 | err = diags | ||
470 | } | ||
471 | |||
472 | return config, err | ||
473 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/merge.go b/vendor/github.com/hashicorp/terraform/config/merge.go index db214be..55fc864 100644 --- a/vendor/github.com/hashicorp/terraform/config/merge.go +++ b/vendor/github.com/hashicorp/terraform/config/merge.go | |||
@@ -137,6 +137,17 @@ func Merge(c1, c2 *Config) (*Config, error) { | |||
137 | } | 137 | } |
138 | } | 138 | } |
139 | 139 | ||
140 | // Local Values | ||
141 | // These are simpler than the other config elements because they are just | ||
142 | // flat values and so no deep merging is required. | ||
143 | if localsCount := len(c1.Locals) + len(c2.Locals); localsCount != 0 { | ||
144 | // Explicit length check above because we want c.Locals to remain | ||
145 | // nil if the result would be empty. | ||
146 | c.Locals = make([]*Local, 0, len(c1.Locals)+len(c2.Locals)) | ||
147 | c.Locals = append(c.Locals, c1.Locals...) | ||
148 | c.Locals = append(c.Locals, c2.Locals...) | ||
149 | } | ||
150 | |||
140 | return c, nil | 151 | return c, nil |
141 | } | 152 | } |
142 | 153 | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/get.go b/vendor/github.com/hashicorp/terraform/config/module/get.go index 96b4a63..5073d0d 100644 --- a/vendor/github.com/hashicorp/terraform/config/module/get.go +++ b/vendor/github.com/hashicorp/terraform/config/module/get.go | |||
@@ -3,6 +3,7 @@ package module | |||
3 | import ( | 3 | import ( |
4 | "io/ioutil" | 4 | "io/ioutil" |
5 | "os" | 5 | "os" |
6 | "path/filepath" | ||
6 | 7 | ||
7 | "github.com/hashicorp/go-getter" | 8 | "github.com/hashicorp/go-getter" |
8 | ) | 9 | ) |
@@ -37,13 +38,10 @@ func GetCopy(dst, src string) error { | |||
37 | if err != nil { | 38 | if err != nil { |
38 | return err | 39 | return err |
39 | } | 40 | } |
40 | // FIXME: This isn't completely safe. Creating and removing our temp path | ||
41 | // exposes where to race to inject files. | ||
42 | if err := os.RemoveAll(tmpDir); err != nil { | ||
43 | return err | ||
44 | } | ||
45 | defer os.RemoveAll(tmpDir) | 41 | defer os.RemoveAll(tmpDir) |
46 | 42 | ||
43 | tmpDir = filepath.Join(tmpDir, "module") | ||
44 | |||
47 | // Get to that temporary dir | 45 | // Get to that temporary dir |
48 | if err := getter.Get(tmpDir, src); err != nil { | 46 | if err := getter.Get(tmpDir, src); err != nil { |
49 | return err | 47 | return err |
@@ -57,15 +55,3 @@ func GetCopy(dst, src string) error { | |||
57 | // Copy to the final location | 55 | // Copy to the final location |
58 | return copyDir(dst, tmpDir) | 56 | return copyDir(dst, tmpDir) |
59 | } | 57 | } |
60 | |||
61 | func getStorage(s getter.Storage, key string, src string, mode GetMode) (string, bool, error) { | ||
62 | // Get the module with the level specified if we were told to. | ||
63 | if mode > GetModeNone { | ||
64 | if err := s.Get(key, src, mode == GetModeUpdate); err != nil { | ||
65 | return "", false, err | ||
66 | } | ||
67 | } | ||
68 | |||
69 | // Get the directory where the module is. | ||
70 | return s.Dir(key) | ||
71 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/inode.go b/vendor/github.com/hashicorp/terraform/config/module/inode.go index 8603ee2..da520ab 100644 --- a/vendor/github.com/hashicorp/terraform/config/module/inode.go +++ b/vendor/github.com/hashicorp/terraform/config/module/inode.go | |||
@@ -1,4 +1,4 @@ | |||
1 | // +build linux darwin openbsd netbsd solaris | 1 | // +build linux darwin openbsd netbsd solaris dragonfly |
2 | 2 | ||
3 | package module | 3 | package module |
4 | 4 | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/module.go b/vendor/github.com/hashicorp/terraform/config/module/module.go index f8649f6..7dc8fcc 100644 --- a/vendor/github.com/hashicorp/terraform/config/module/module.go +++ b/vendor/github.com/hashicorp/terraform/config/module/module.go | |||
@@ -2,6 +2,8 @@ package module | |||
2 | 2 | ||
3 | // Module represents the metadata for a single module. | 3 | // Module represents the metadata for a single module. |
4 | type Module struct { | 4 | type Module struct { |
5 | Name string | 5 | Name string |
6 | Source string | 6 | Source string |
7 | Version string | ||
8 | Providers map[string]string | ||
7 | } | 9 | } |
diff --git a/vendor/github.com/hashicorp/terraform/config/module/storage.go b/vendor/github.com/hashicorp/terraform/config/module/storage.go new file mode 100644 index 0000000..58e3a10 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/storage.go | |||
@@ -0,0 +1,365 @@ | |||
1 | package module | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | "io/ioutil" | ||
7 | "log" | ||
8 | "os" | ||
9 | "path/filepath" | ||
10 | "strings" | ||
11 | |||
12 | getter "github.com/hashicorp/go-getter" | ||
13 | "github.com/hashicorp/terraform/registry" | ||
14 | "github.com/hashicorp/terraform/registry/regsrc" | ||
15 | "github.com/hashicorp/terraform/svchost/disco" | ||
16 | "github.com/mitchellh/cli" | ||
17 | ) | ||
18 | |||
19 | const manifestName = "modules.json" | ||
20 | |||
21 | // moduleManifest is the serialization structure used to record the stored | ||
22 | // module's metadata. | ||
23 | type moduleManifest struct { | ||
24 | Modules []moduleRecord | ||
25 | } | ||
26 | |||
27 | // moduleRecords represents the stored module's metadata. | ||
28 | // This is compared for equality using '==', so all fields needs to remain | ||
29 | // comparable. | ||
30 | type moduleRecord struct { | ||
31 | // Source is the module source string from the config, minus any | ||
32 | // subdirectory. | ||
33 | Source string | ||
34 | |||
35 | // Key is the locally unique identifier for this module. | ||
36 | Key string | ||
37 | |||
38 | // Version is the exact version string for the stored module. | ||
39 | Version string | ||
40 | |||
41 | // Dir is the directory name returned by the FileStorage. This is what | ||
42 | // allows us to correlate a particular module version with the location on | ||
43 | // disk. | ||
44 | Dir string | ||
45 | |||
46 | // Root is the root directory containing the module. If the module is | ||
47 | // unpacked from an archive, and not located in the root directory, this is | ||
48 | // used to direct the loader to the correct subdirectory. This is | ||
49 | // independent from any subdirectory in the original source string, which | ||
50 | // may traverse further into the module tree. | ||
51 | Root string | ||
52 | |||
53 | // url is the location of the module source | ||
54 | url string | ||
55 | |||
56 | // Registry is true if this module is sourced from a registry | ||
57 | registry bool | ||
58 | } | ||
59 | |||
60 | // Storage implements methods to manage the storage of modules. | ||
61 | // This is used by Tree.Load to query registries, authenticate requests, and | ||
62 | // store modules locally. | ||
63 | type Storage struct { | ||
64 | // StorageDir is the full path to the directory where all modules will be | ||
65 | // stored. | ||
66 | StorageDir string | ||
67 | |||
68 | // Ui is an optional cli.Ui for user output | ||
69 | Ui cli.Ui | ||
70 | |||
71 | // Mode is the GetMode that will be used for various operations. | ||
72 | Mode GetMode | ||
73 | |||
74 | registry *registry.Client | ||
75 | } | ||
76 | |||
77 | // NewStorage returns a new initialized Storage object. | ||
78 | func NewStorage(dir string, services *disco.Disco) *Storage { | ||
79 | regClient := registry.NewClient(services, nil) | ||
80 | |||
81 | return &Storage{ | ||
82 | StorageDir: dir, | ||
83 | registry: regClient, | ||
84 | } | ||
85 | } | ||
86 | |||
87 | // loadManifest returns the moduleManifest file from the parent directory. | ||
88 | func (s Storage) loadManifest() (moduleManifest, error) { | ||
89 | manifest := moduleManifest{} | ||
90 | |||
91 | manifestPath := filepath.Join(s.StorageDir, manifestName) | ||
92 | data, err := ioutil.ReadFile(manifestPath) | ||
93 | if err != nil && !os.IsNotExist(err) { | ||
94 | return manifest, err | ||
95 | } | ||
96 | |||
97 | if len(data) == 0 { | ||
98 | return manifest, nil | ||
99 | } | ||
100 | |||
101 | if err := json.Unmarshal(data, &manifest); err != nil { | ||
102 | return manifest, err | ||
103 | } | ||
104 | |||
105 | for i, rec := range manifest.Modules { | ||
106 | // If the path was recorded before we changed to always using a | ||
107 | // slash as separator, we delete the record from the manifest so | ||
108 | // it can be discovered again and will be recorded using a slash. | ||
109 | if strings.Contains(rec.Dir, "\\") { | ||
110 | manifest.Modules[i] = manifest.Modules[len(manifest.Modules)-1] | ||
111 | manifest.Modules = manifest.Modules[:len(manifest.Modules)-1] | ||
112 | continue | ||
113 | } | ||
114 | |||
115 | // Make sure we use the correct path separator. | ||
116 | rec.Dir = filepath.FromSlash(rec.Dir) | ||
117 | } | ||
118 | |||
119 | return manifest, nil | ||
120 | } | ||
121 | |||
122 | // Store the location of the module, along with the version used and the module | ||
123 | // root directory. The storage method loads the entire file and rewrites it | ||
124 | // each time. This is only done a few times during init, so efficiency is | ||
125 | // not a concern. | ||
126 | func (s Storage) recordModule(rec moduleRecord) error { | ||
127 | manifest, err := s.loadManifest() | ||
128 | if err != nil { | ||
129 | // if there was a problem with the file, we will attempt to write a new | ||
130 | // one. Any non-data related error should surface there. | ||
131 | log.Printf("[WARN] error reading module manifest: %s", err) | ||
132 | } | ||
133 | |||
134 | // do nothing if we already have the exact module | ||
135 | for i, stored := range manifest.Modules { | ||
136 | if rec == stored { | ||
137 | return nil | ||
138 | } | ||
139 | |||
140 | // they are not equal, but if the storage path is the same we need to | ||
141 | // remove this rec to be replaced. | ||
142 | if rec.Dir == stored.Dir { | ||
143 | manifest.Modules[i] = manifest.Modules[len(manifest.Modules)-1] | ||
144 | manifest.Modules = manifest.Modules[:len(manifest.Modules)-1] | ||
145 | break | ||
146 | } | ||
147 | } | ||
148 | |||
149 | // Make sure we always use a slash separator. | ||
150 | rec.Dir = filepath.ToSlash(rec.Dir) | ||
151 | |||
152 | manifest.Modules = append(manifest.Modules, rec) | ||
153 | |||
154 | js, err := json.Marshal(manifest) | ||
155 | if err != nil { | ||
156 | panic(err) | ||
157 | } | ||
158 | |||
159 | manifestPath := filepath.Join(s.StorageDir, manifestName) | ||
160 | return ioutil.WriteFile(manifestPath, js, 0644) | ||
161 | } | ||
162 | |||
163 | // load the manifest from dir, and return all module versions matching the | ||
164 | // provided source. Records with no version info will be skipped, as they need | ||
165 | // to be uniquely identified by other means. | ||
166 | func (s Storage) moduleVersions(source string) ([]moduleRecord, error) { | ||
167 | manifest, err := s.loadManifest() | ||
168 | if err != nil { | ||
169 | return manifest.Modules, err | ||
170 | } | ||
171 | |||
172 | var matching []moduleRecord | ||
173 | |||
174 | for _, m := range manifest.Modules { | ||
175 | if m.Source == source && m.Version != "" { | ||
176 | log.Printf("[DEBUG] found local version %q for module %s", m.Version, m.Source) | ||
177 | matching = append(matching, m) | ||
178 | } | ||
179 | } | ||
180 | |||
181 | return matching, nil | ||
182 | } | ||
183 | |||
184 | func (s Storage) moduleDir(key string) (string, error) { | ||
185 | manifest, err := s.loadManifest() | ||
186 | if err != nil { | ||
187 | return "", err | ||
188 | } | ||
189 | |||
190 | for _, m := range manifest.Modules { | ||
191 | if m.Key == key { | ||
192 | return m.Dir, nil | ||
193 | } | ||
194 | } | ||
195 | |||
196 | return "", nil | ||
197 | } | ||
198 | |||
199 | // return only the root directory of the module stored in dir. | ||
200 | func (s Storage) getModuleRoot(dir string) (string, error) { | ||
201 | manifest, err := s.loadManifest() | ||
202 | if err != nil { | ||
203 | return "", err | ||
204 | } | ||
205 | |||
206 | for _, mod := range manifest.Modules { | ||
207 | if mod.Dir == dir { | ||
208 | return mod.Root, nil | ||
209 | } | ||
210 | } | ||
211 | return "", nil | ||
212 | } | ||
213 | |||
214 | // record only the Root directory for the module stored at dir. | ||
215 | func (s Storage) recordModuleRoot(dir, root string) error { | ||
216 | rec := moduleRecord{ | ||
217 | Dir: dir, | ||
218 | Root: root, | ||
219 | } | ||
220 | |||
221 | return s.recordModule(rec) | ||
222 | } | ||
223 | |||
224 | func (s Storage) output(msg string) { | ||
225 | if s.Ui == nil || s.Mode == GetModeNone { | ||
226 | return | ||
227 | } | ||
228 | s.Ui.Output(msg) | ||
229 | } | ||
230 | |||
231 | func (s Storage) getStorage(key string, src string) (string, bool, error) { | ||
232 | storage := &getter.FolderStorage{ | ||
233 | StorageDir: s.StorageDir, | ||
234 | } | ||
235 | |||
236 | log.Printf("[DEBUG] fetching module from %s", src) | ||
237 | |||
238 | // Get the module with the level specified if we were told to. | ||
239 | if s.Mode > GetModeNone { | ||
240 | log.Printf("[DEBUG] fetching %q with key %q", src, key) | ||
241 | if err := storage.Get(key, src, s.Mode == GetModeUpdate); err != nil { | ||
242 | return "", false, err | ||
243 | } | ||
244 | } | ||
245 | |||
246 | // Get the directory where the module is. | ||
247 | dir, found, err := storage.Dir(key) | ||
248 | log.Printf("[DEBUG] found %q in %q: %t", src, dir, found) | ||
249 | return dir, found, err | ||
250 | } | ||
251 | |||
252 | // find a stored module that's not from a registry | ||
253 | func (s Storage) findModule(key string) (string, error) { | ||
254 | if s.Mode == GetModeUpdate { | ||
255 | return "", nil | ||
256 | } | ||
257 | |||
258 | return s.moduleDir(key) | ||
259 | } | ||
260 | |||
261 | // GetModule fetches a module source into the specified directory. This is used | ||
262 | // as a convenience function by the CLI to initialize a configuration. | ||
263 | func (s Storage) GetModule(dst, src string) error { | ||
264 | // reset this in case the caller was going to re-use it | ||
265 | mode := s.Mode | ||
266 | s.Mode = GetModeUpdate | ||
267 | defer func() { | ||
268 | s.Mode = mode | ||
269 | }() | ||
270 | |||
271 | rec, err := s.findRegistryModule(src, anyVersion) | ||
272 | if err != nil { | ||
273 | return err | ||
274 | } | ||
275 | |||
276 | pwd, err := os.Getwd() | ||
277 | if err != nil { | ||
278 | return err | ||
279 | } | ||
280 | |||
281 | source := rec.url | ||
282 | if source == "" { | ||
283 | source, err = getter.Detect(src, pwd, getter.Detectors) | ||
284 | if err != nil { | ||
285 | return fmt.Errorf("module %s: %s", src, err) | ||
286 | } | ||
287 | } | ||
288 | |||
289 | if source == "" { | ||
290 | return fmt.Errorf("module %q not found", src) | ||
291 | } | ||
292 | |||
293 | return GetCopy(dst, source) | ||
294 | } | ||
295 | |||
296 | // find a registry module | ||
297 | func (s Storage) findRegistryModule(mSource, constraint string) (moduleRecord, error) { | ||
298 | rec := moduleRecord{ | ||
299 | Source: mSource, | ||
300 | } | ||
301 | // detect if we have a registry source | ||
302 | mod, err := regsrc.ParseModuleSource(mSource) | ||
303 | switch err { | ||
304 | case nil: | ||
305 | //ok | ||
306 | case regsrc.ErrInvalidModuleSource: | ||
307 | return rec, nil | ||
308 | default: | ||
309 | return rec, err | ||
310 | } | ||
311 | rec.registry = true | ||
312 | |||
313 | log.Printf("[TRACE] %q is a registry module", mod.Display()) | ||
314 | |||
315 | versions, err := s.moduleVersions(mod.String()) | ||
316 | if err != nil { | ||
317 | log.Printf("[ERROR] error looking up versions for %q: %s", mod.Display(), err) | ||
318 | return rec, err | ||
319 | } | ||
320 | |||
321 | match, err := newestRecord(versions, constraint) | ||
322 | if err != nil { | ||
323 | log.Printf("[INFO] no matching version for %q<%s>, %s", mod.Display(), constraint, err) | ||
324 | } | ||
325 | log.Printf("[DEBUG] matched %q version %s for %s", mod, match.Version, constraint) | ||
326 | |||
327 | rec.Dir = match.Dir | ||
328 | rec.Version = match.Version | ||
329 | found := rec.Dir != "" | ||
330 | |||
331 | // we need to lookup available versions | ||
332 | // Only on Get if it's not found, on unconditionally on Update | ||
333 | if (s.Mode == GetModeGet && !found) || (s.Mode == GetModeUpdate) { | ||
334 | resp, err := s.registry.Versions(mod) | ||
335 | if err != nil { | ||
336 | return rec, err | ||
337 | } | ||
338 | |||
339 | if len(resp.Modules) == 0 { | ||
340 | return rec, fmt.Errorf("module %q not found in registry", mod.Display()) | ||
341 | } | ||
342 | |||
343 | match, err := newestVersion(resp.Modules[0].Versions, constraint) | ||
344 | if err != nil { | ||
345 | return rec, err | ||
346 | } | ||
347 | |||
348 | if match == nil { | ||
349 | return rec, fmt.Errorf("no versions for %q found matching %q", mod.Display(), constraint) | ||
350 | } | ||
351 | |||
352 | rec.Version = match.Version | ||
353 | |||
354 | rec.url, err = s.registry.Location(mod, rec.Version) | ||
355 | if err != nil { | ||
356 | return rec, err | ||
357 | } | ||
358 | |||
359 | // we've already validated this by now | ||
360 | host, _ := mod.SvcHost() | ||
361 | s.output(fmt.Sprintf(" Found version %s of %s on %s", rec.Version, mod.Module(), host.ForDisplay())) | ||
362 | |||
363 | } | ||
364 | return rec, nil | ||
365 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/module/testing.go b/vendor/github.com/hashicorp/terraform/config/module/testing.go index fc9e733..6f1ff05 100644 --- a/vendor/github.com/hashicorp/terraform/config/module/testing.go +++ b/vendor/github.com/hashicorp/terraform/config/module/testing.go | |||
@@ -4,8 +4,6 @@ import ( | |||
4 | "io/ioutil" | 4 | "io/ioutil" |
5 | "os" | 5 | "os" |
6 | "testing" | 6 | "testing" |
7 | |||
8 | "github.com/hashicorp/go-getter" | ||
9 | ) | 7 | ) |
10 | 8 | ||
11 | // TestTree loads a module at the given path and returns the tree as well | 9 | // TestTree loads a module at the given path and returns the tree as well |
@@ -26,8 +24,8 @@ func TestTree(t *testing.T, path string) (*Tree, func()) { | |||
26 | } | 24 | } |
27 | 25 | ||
28 | // Get the child modules | 26 | // Get the child modules |
29 | s := &getter.FolderStorage{StorageDir: dir} | 27 | s := &Storage{StorageDir: dir, Mode: GetModeGet} |
30 | if err := mod.Load(s, GetModeGet); err != nil { | 28 | if err := mod.Load(s); err != nil { |
31 | t.Fatalf("err: %s", err) | 29 | t.Fatalf("err: %s", err) |
32 | return nil, nil | 30 | return nil, nil |
33 | } | 31 | } |
diff --git a/vendor/github.com/hashicorp/terraform/config/module/tree.go b/vendor/github.com/hashicorp/terraform/config/module/tree.go index 4b0b153..f56d69b 100644 --- a/vendor/github.com/hashicorp/terraform/config/module/tree.go +++ b/vendor/github.com/hashicorp/terraform/config/module/tree.go | |||
@@ -4,11 +4,14 @@ import ( | |||
4 | "bufio" | 4 | "bufio" |
5 | "bytes" | 5 | "bytes" |
6 | "fmt" | 6 | "fmt" |
7 | "log" | ||
7 | "path/filepath" | 8 | "path/filepath" |
8 | "strings" | 9 | "strings" |
9 | "sync" | 10 | "sync" |
10 | 11 | ||
11 | "github.com/hashicorp/go-getter" | 12 | "github.com/hashicorp/terraform/tfdiags" |
13 | |||
14 | getter "github.com/hashicorp/go-getter" | ||
12 | "github.com/hashicorp/terraform/config" | 15 | "github.com/hashicorp/terraform/config" |
13 | ) | 16 | ) |
14 | 17 | ||
@@ -26,6 +29,17 @@ type Tree struct { | |||
26 | children map[string]*Tree | 29 | children map[string]*Tree |
27 | path []string | 30 | path []string |
28 | lock sync.RWMutex | 31 | lock sync.RWMutex |
32 | |||
33 | // version is the final version of the config loaded for the Tree's module | ||
34 | version string | ||
35 | // source is the "source" string used to load this module. It's possible | ||
36 | // for a module source to change, but the path remains the same, preventing | ||
37 | // it from being reloaded. | ||
38 | source string | ||
39 | // parent allows us to walk back up the tree and determine if there are any | ||
40 | // versioned ancestor modules which may effect the stored location of | ||
41 | // submodules | ||
42 | parent *Tree | ||
29 | } | 43 | } |
30 | 44 | ||
31 | // NewTree returns a new Tree for the given config structure. | 45 | // NewTree returns a new Tree for the given config structure. |
@@ -40,7 +54,7 @@ func NewEmptyTree() *Tree { | |||
40 | // We do this dummy load so that the tree is marked as "loaded". It | 54 | // We do this dummy load so that the tree is marked as "loaded". It |
41 | // should never fail because this is just about a no-op. If it does fail | 55 | // should never fail because this is just about a no-op. If it does fail |
42 | // we panic so we can know its a bug. | 56 | // we panic so we can know its a bug. |
43 | if err := t.Load(nil, GetModeGet); err != nil { | 57 | if err := t.Load(&Storage{Mode: GetModeGet}); err != nil { |
44 | panic(err) | 58 | panic(err) |
45 | } | 59 | } |
46 | 60 | ||
@@ -126,8 +140,10 @@ func (t *Tree) Modules() []*Module { | |||
126 | result := make([]*Module, len(t.config.Modules)) | 140 | result := make([]*Module, len(t.config.Modules)) |
127 | for i, m := range t.config.Modules { | 141 | for i, m := range t.config.Modules { |
128 | result[i] = &Module{ | 142 | result[i] = &Module{ |
129 | Name: m.Name, | 143 | Name: m.Name, |
130 | Source: m.Source, | 144 | Version: m.Version, |
145 | Source: m.Source, | ||
146 | Providers: m.Providers, | ||
131 | } | 147 | } |
132 | } | 148 | } |
133 | 149 | ||
@@ -155,81 +171,178 @@ func (t *Tree) Name() string { | |||
155 | // module trees inherently require the configuration to be in a reasonably | 171 | // module trees inherently require the configuration to be in a reasonably |
156 | // sane state: no circular dependencies, proper module sources, etc. A full | 172 | // sane state: no circular dependencies, proper module sources, etc. A full |
157 | // suite of validations can be done by running Validate (after loading). | 173 | // suite of validations can be done by running Validate (after loading). |
158 | func (t *Tree) Load(s getter.Storage, mode GetMode) error { | 174 | func (t *Tree) Load(s *Storage) error { |
159 | t.lock.Lock() | 175 | t.lock.Lock() |
160 | defer t.lock.Unlock() | 176 | defer t.lock.Unlock() |
161 | 177 | ||
162 | // Reset the children if we have any | 178 | children, err := t.getChildren(s) |
163 | t.children = nil | 179 | if err != nil { |
180 | return err | ||
181 | } | ||
182 | |||
183 | // Go through all the children and load them. | ||
184 | for _, c := range children { | ||
185 | if err := c.Load(s); err != nil { | ||
186 | return err | ||
187 | } | ||
188 | } | ||
189 | |||
190 | // Set our tree up | ||
191 | t.children = children | ||
164 | 192 | ||
165 | modules := t.Modules() | 193 | return nil |
194 | } | ||
195 | |||
196 | func (t *Tree) getChildren(s *Storage) (map[string]*Tree, error) { | ||
166 | children := make(map[string]*Tree) | 197 | children := make(map[string]*Tree) |
167 | 198 | ||
168 | // Go through all the modules and get the directory for them. | 199 | // Go through all the modules and get the directory for them. |
169 | for _, m := range modules { | 200 | for _, m := range t.Modules() { |
170 | if _, ok := children[m.Name]; ok { | 201 | if _, ok := children[m.Name]; ok { |
171 | return fmt.Errorf( | 202 | return nil, fmt.Errorf( |
172 | "module %s: duplicated. module names must be unique", m.Name) | 203 | "module %s: duplicated. module names must be unique", m.Name) |
173 | } | 204 | } |
174 | 205 | ||
175 | // Determine the path to this child | 206 | // Determine the path to this child |
176 | path := make([]string, len(t.path), len(t.path)+1) | 207 | modPath := make([]string, len(t.path), len(t.path)+1) |
177 | copy(path, t.path) | 208 | copy(modPath, t.path) |
178 | path = append(path, m.Name) | 209 | modPath = append(modPath, m.Name) |
179 | 210 | ||
180 | // Split out the subdir if we have one | 211 | log.Printf("[TRACE] module source: %q", m.Source) |
181 | source, subDir := getter.SourceDirSubdir(m.Source) | ||
182 | 212 | ||
183 | source, err := getter.Detect(source, t.config.Dir, getter.Detectors) | 213 | // add the module path to help indicate where modules with relative |
214 | // paths are being loaded from | ||
215 | s.output(fmt.Sprintf("- module.%s", strings.Join(modPath, "."))) | ||
216 | |||
217 | // Lookup the local location of the module. | ||
218 | // dir is the local directory where the module is stored | ||
219 | mod, err := s.findRegistryModule(m.Source, m.Version) | ||
184 | if err != nil { | 220 | if err != nil { |
185 | return fmt.Errorf("module %s: %s", m.Name, err) | 221 | return nil, err |
186 | } | 222 | } |
187 | 223 | ||
224 | // The key is the string that will be used to uniquely id the Source in | ||
225 | // the local storage. The prefix digit can be incremented to | ||
226 | // invalidate the local module storage. | ||
227 | key := "1." + t.versionedPathKey(m) | ||
228 | if mod.Version != "" { | ||
229 | key += "." + mod.Version | ||
230 | } | ||
231 | |||
232 | // Check for the exact key if it's not a registry module | ||
233 | if !mod.registry { | ||
234 | mod.Dir, err = s.findModule(key) | ||
235 | if err != nil { | ||
236 | return nil, err | ||
237 | } | ||
238 | } | ||
239 | |||
240 | if mod.Dir != "" && s.Mode != GetModeUpdate { | ||
241 | // We found it locally, but in order to load the Tree we need to | ||
242 | // find out if there was another subDir stored from detection. | ||
243 | subDir, err := s.getModuleRoot(mod.Dir) | ||
244 | if err != nil { | ||
245 | // If there's a problem with the subdir record, we'll let the | ||
246 | // recordSubdir method fix it up. Any other filesystem errors | ||
247 | // will turn up again below. | ||
248 | log.Println("[WARN] error reading subdir record:", err) | ||
249 | } | ||
250 | |||
251 | fullDir := filepath.Join(mod.Dir, subDir) | ||
252 | |||
253 | child, err := NewTreeModule(m.Name, fullDir) | ||
254 | if err != nil { | ||
255 | return nil, fmt.Errorf("module %s: %s", m.Name, err) | ||
256 | } | ||
257 | child.path = modPath | ||
258 | child.parent = t | ||
259 | child.version = mod.Version | ||
260 | child.source = m.Source | ||
261 | children[m.Name] = child | ||
262 | continue | ||
263 | } | ||
264 | |||
265 | // Split out the subdir if we have one. | ||
266 | // Terraform keeps the entire requested tree, so that modules can | ||
267 | // reference sibling modules from the same archive or repo. | ||
268 | rawSource, subDir := getter.SourceDirSubdir(m.Source) | ||
269 | |||
270 | // we haven't found a source, so fallback to the go-getter detectors | ||
271 | source := mod.url | ||
272 | if source == "" { | ||
273 | source, err = getter.Detect(rawSource, t.config.Dir, getter.Detectors) | ||
274 | if err != nil { | ||
275 | return nil, fmt.Errorf("module %s: %s", m.Name, err) | ||
276 | } | ||
277 | } | ||
278 | |||
279 | log.Printf("[TRACE] detected module source %q", source) | ||
280 | |||
188 | // Check if the detector introduced something new. | 281 | // Check if the detector introduced something new. |
189 | source, subDir2 := getter.SourceDirSubdir(source) | 282 | // For example, the registry always adds a subdir of `//*`, |
190 | if subDir2 != "" { | 283 | // indicating that we need to strip off the first component from the |
191 | subDir = filepath.Join(subDir2, subDir) | 284 | // tar archive, though we may not yet know what it is called. |
285 | source, detectedSubDir := getter.SourceDirSubdir(source) | ||
286 | if detectedSubDir != "" { | ||
287 | subDir = filepath.Join(detectedSubDir, subDir) | ||
288 | } | ||
289 | |||
290 | output := "" | ||
291 | switch s.Mode { | ||
292 | case GetModeUpdate: | ||
293 | output = fmt.Sprintf(" Updating source %q", m.Source) | ||
294 | default: | ||
295 | output = fmt.Sprintf(" Getting source %q", m.Source) | ||
192 | } | 296 | } |
297 | s.output(output) | ||
193 | 298 | ||
194 | // Get the directory where this module is so we can load it | 299 | dir, ok, err := s.getStorage(key, source) |
195 | key := strings.Join(path, ".") | ||
196 | key = fmt.Sprintf("root.%s-%s", key, m.Source) | ||
197 | dir, ok, err := getStorage(s, key, source, mode) | ||
198 | if err != nil { | 300 | if err != nil { |
199 | return err | 301 | return nil, err |
200 | } | 302 | } |
201 | if !ok { | 303 | if !ok { |
202 | return fmt.Errorf( | 304 | return nil, fmt.Errorf("module %s: not found, may need to run 'terraform init'", m.Name) |
203 | "module %s: not found, may need to be downloaded using 'terraform get'", m.Name) | ||
204 | } | 305 | } |
205 | 306 | ||
206 | // If we have a subdirectory, then merge that in | 307 | log.Printf("[TRACE] %q stored in %q", source, dir) |
308 | |||
309 | // expand and record the subDir for later | ||
310 | fullDir := dir | ||
207 | if subDir != "" { | 311 | if subDir != "" { |
208 | dir = filepath.Join(dir, subDir) | 312 | fullDir, err = getter.SubdirGlob(dir, subDir) |
209 | } | 313 | if err != nil { |
314 | return nil, err | ||
315 | } | ||
210 | 316 | ||
211 | // Load the configurations.Dir(source) | 317 | // +1 to account for the pathsep |
212 | children[m.Name], err = NewTreeModule(m.Name, dir) | 318 | if len(dir)+1 > len(fullDir) { |
213 | if err != nil { | 319 | return nil, fmt.Errorf("invalid module storage path %q", fullDir) |
214 | return fmt.Errorf( | 320 | } |
215 | "module %s: %s", m.Name, err) | 321 | subDir = fullDir[len(dir)+1:] |
216 | } | 322 | } |
217 | 323 | ||
218 | // Set the path of this child | 324 | // add new info to the module record |
219 | children[m.Name].path = path | 325 | mod.Key = key |
220 | } | 326 | mod.Dir = dir |
327 | mod.Root = subDir | ||
221 | 328 | ||
222 | // Go through all the children and load them. | 329 | // record the module in our manifest |
223 | for _, c := range children { | 330 | if err := s.recordModule(mod); err != nil { |
224 | if err := c.Load(s, mode); err != nil { | 331 | return nil, err |
225 | return err | ||
226 | } | 332 | } |
227 | } | ||
228 | 333 | ||
229 | // Set our tree up | 334 | child, err := NewTreeModule(m.Name, fullDir) |
230 | t.children = children | 335 | if err != nil { |
336 | return nil, fmt.Errorf("module %s: %s", m.Name, err) | ||
337 | } | ||
338 | child.path = modPath | ||
339 | child.parent = t | ||
340 | child.version = mod.Version | ||
341 | child.source = m.Source | ||
342 | children[m.Name] = child | ||
343 | } | ||
231 | 344 | ||
232 | return nil | 345 | return children, nil |
233 | } | 346 | } |
234 | 347 | ||
235 | // Path is the full path to this tree. | 348 | // Path is the full path to this tree. |
@@ -272,32 +385,35 @@ func (t *Tree) String() string { | |||
272 | // as verifying things such as parameters/outputs between the various modules. | 385 | // as verifying things such as parameters/outputs between the various modules. |
273 | // | 386 | // |
274 | // Load must be called prior to calling Validate or an error will be returned. | 387 | // Load must be called prior to calling Validate or an error will be returned. |
275 | func (t *Tree) Validate() error { | 388 | func (t *Tree) Validate() tfdiags.Diagnostics { |
389 | var diags tfdiags.Diagnostics | ||
390 | |||
276 | if !t.Loaded() { | 391 | if !t.Loaded() { |
277 | return fmt.Errorf("tree must be loaded before calling Validate") | 392 | diags = diags.Append(fmt.Errorf( |
393 | "tree must be loaded before calling Validate", | ||
394 | )) | ||
395 | return diags | ||
278 | } | 396 | } |
279 | 397 | ||
280 | // If something goes wrong, here is our error template | ||
281 | newErr := &treeError{Name: []string{t.Name()}} | ||
282 | |||
283 | // Terraform core does not handle root module children named "root". | 398 | // Terraform core does not handle root module children named "root". |
284 | // We plan to fix this in the future but this bug was brought up in | 399 | // We plan to fix this in the future but this bug was brought up in |
285 | // the middle of a release and we don't want to introduce wide-sweeping | 400 | // the middle of a release and we don't want to introduce wide-sweeping |
286 | // changes at that time. | 401 | // changes at that time. |
287 | if len(t.path) == 1 && t.name == "root" { | 402 | if len(t.path) == 1 && t.name == "root" { |
288 | return fmt.Errorf("root module cannot contain module named 'root'") | 403 | diags = diags.Append(fmt.Errorf( |
404 | "root module cannot contain module named 'root'", | ||
405 | )) | ||
406 | return diags | ||
289 | } | 407 | } |
290 | 408 | ||
291 | // Validate our configuration first. | 409 | // Validate our configuration first. |
292 | if err := t.config.Validate(); err != nil { | 410 | diags = diags.Append(t.config.Validate()) |
293 | newErr.Add(err) | ||
294 | } | ||
295 | 411 | ||
296 | // If we're the root, we do extra validation. This validation usually | 412 | // If we're the root, we do extra validation. This validation usually |
297 | // requires the entire tree (since children don't have parent pointers). | 413 | // requires the entire tree (since children don't have parent pointers). |
298 | if len(t.path) == 0 { | 414 | if len(t.path) == 0 { |
299 | if err := t.validateProviderAlias(); err != nil { | 415 | if err := t.validateProviderAlias(); err != nil { |
300 | newErr.Add(err) | 416 | diags = diags.Append(err) |
301 | } | 417 | } |
302 | } | 418 | } |
303 | 419 | ||
@@ -306,20 +422,11 @@ func (t *Tree) Validate() error { | |||
306 | 422 | ||
307 | // Validate all our children | 423 | // Validate all our children |
308 | for _, c := range children { | 424 | for _, c := range children { |
309 | err := c.Validate() | 425 | childDiags := c.Validate() |
310 | if err == nil { | 426 | diags = diags.Append(childDiags) |
427 | if diags.HasErrors() { | ||
311 | continue | 428 | continue |
312 | } | 429 | } |
313 | |||
314 | verr, ok := err.(*treeError) | ||
315 | if !ok { | ||
316 | // Unknown error, just return... | ||
317 | return err | ||
318 | } | ||
319 | |||
320 | // Append ourselves to the error and then return | ||
321 | verr.Name = append(verr.Name, t.Name()) | ||
322 | newErr.AddChild(verr) | ||
323 | } | 430 | } |
324 | 431 | ||
325 | // Go over all the modules and verify that any parameters are valid | 432 | // Go over all the modules and verify that any parameters are valid |
@@ -345,9 +452,10 @@ func (t *Tree) Validate() error { | |||
345 | // Compare to the keys in our raw config for the module | 452 | // Compare to the keys in our raw config for the module |
346 | for k, _ := range m.RawConfig.Raw { | 453 | for k, _ := range m.RawConfig.Raw { |
347 | if _, ok := varMap[k]; !ok { | 454 | if _, ok := varMap[k]; !ok { |
348 | newErr.Add(fmt.Errorf( | 455 | diags = diags.Append(fmt.Errorf( |
349 | "module %s: %s is not a valid parameter", | 456 | "module %q: %q is not a valid argument", |
350 | m.Name, k)) | 457 | m.Name, k, |
458 | )) | ||
351 | } | 459 | } |
352 | 460 | ||
353 | // Remove the required | 461 | // Remove the required |
@@ -356,9 +464,10 @@ func (t *Tree) Validate() error { | |||
356 | 464 | ||
357 | // If we have any required left over, they aren't set. | 465 | // If we have any required left over, they aren't set. |
358 | for k, _ := range requiredMap { | 466 | for k, _ := range requiredMap { |
359 | newErr.Add(fmt.Errorf( | 467 | diags = diags.Append(fmt.Errorf( |
360 | "module %s: required variable %q not set", | 468 | "module %q: missing required argument %q", |
361 | m.Name, k)) | 469 | m.Name, k, |
470 | )) | ||
362 | } | 471 | } |
363 | } | 472 | } |
364 | 473 | ||
@@ -373,9 +482,10 @@ func (t *Tree) Validate() error { | |||
373 | 482 | ||
374 | tree, ok := children[mv.Name] | 483 | tree, ok := children[mv.Name] |
375 | if !ok { | 484 | if !ok { |
376 | newErr.Add(fmt.Errorf( | 485 | diags = diags.Append(fmt.Errorf( |
377 | "%s: undefined module referenced %s", | 486 | "%s: reference to undefined module %q", |
378 | source, mv.Name)) | 487 | source, mv.Name, |
488 | )) | ||
379 | continue | 489 | continue |
380 | } | 490 | } |
381 | 491 | ||
@@ -387,14 +497,56 @@ func (t *Tree) Validate() error { | |||
387 | } | 497 | } |
388 | } | 498 | } |
389 | if !found { | 499 | if !found { |
390 | newErr.Add(fmt.Errorf( | 500 | diags = diags.Append(fmt.Errorf( |
391 | "%s: %s is not a valid output for module %s", | 501 | "%s: %q is not a valid output for module %q", |
392 | source, mv.Field, mv.Name)) | 502 | source, mv.Field, mv.Name, |
503 | )) | ||
393 | } | 504 | } |
394 | } | 505 | } |
395 | } | 506 | } |
396 | 507 | ||
397 | return newErr.ErrOrNil() | 508 | return diags |
509 | } | ||
510 | |||
511 | // versionedPathKey returns a path string with every levels full name, version | ||
512 | // and source encoded. This is to provide a unique key for our module storage, | ||
513 | // since submodules need to know which versions of their ancestor modules they | ||
514 | // are loaded from. | ||
515 | // For example, if module A has a subdirectory B, if module A's source or | ||
516 | // version is updated B's storage key must reflect this change in order for the | ||
517 | // correct version of B's source to be loaded. | ||
518 | func (t *Tree) versionedPathKey(m *Module) string { | ||
519 | path := make([]string, len(t.path)+1) | ||
520 | path[len(path)-1] = m.Name + ";" + m.Source | ||
521 | // We're going to load these in order for easier reading and debugging, but | ||
522 | // in practice they only need to be unique and consistent. | ||
523 | |||
524 | p := t | ||
525 | i := len(path) - 2 | ||
526 | for ; i >= 0; i-- { | ||
527 | if p == nil { | ||
528 | break | ||
529 | } | ||
530 | // we may have been loaded under a blank Tree, so always check for a name | ||
531 | // too. | ||
532 | if p.name == "" { | ||
533 | break | ||
534 | } | ||
535 | seg := p.name | ||
536 | if p.version != "" { | ||
537 | seg += "#" + p.version | ||
538 | } | ||
539 | |||
540 | if p.source != "" { | ||
541 | seg += ";" + p.source | ||
542 | } | ||
543 | |||
544 | path[i] = seg | ||
545 | p = p.parent | ||
546 | } | ||
547 | |||
548 | key := strings.Join(path, "|") | ||
549 | return key | ||
398 | } | 550 | } |
399 | 551 | ||
400 | // treeError is an error use by Tree.Validate to accumulates all | 552 | // treeError is an error use by Tree.Validate to accumulates all |
diff --git a/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go b/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go index 090d4f7..f203556 100644 --- a/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go +++ b/vendor/github.com/hashicorp/terraform/config/module/validate_provider_alias.go | |||
@@ -67,7 +67,7 @@ func (t *Tree) validateProviderAlias() error { | |||
67 | 67 | ||
68 | // We didn't find the alias, error! | 68 | // We didn't find the alias, error! |
69 | err = multierror.Append(err, fmt.Errorf( | 69 | err = multierror.Append(err, fmt.Errorf( |
70 | "module %s: provider alias must be defined by the module or a parent: %s", | 70 | "module %s: provider alias must be defined by the module: %s", |
71 | strings.Join(pv.Path, "."), k)) | 71 | strings.Join(pv.Path, "."), k)) |
72 | } | 72 | } |
73 | } | 73 | } |
diff --git a/vendor/github.com/hashicorp/terraform/config/module/versions.go b/vendor/github.com/hashicorp/terraform/config/module/versions.go new file mode 100644 index 0000000..8348d4b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/config/module/versions.go | |||
@@ -0,0 +1,95 @@ | |||
1 | package module | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | "sort" | ||
7 | |||
8 | version "github.com/hashicorp/go-version" | ||
9 | "github.com/hashicorp/terraform/registry/response" | ||
10 | ) | ||
11 | |||
12 | const anyVersion = ">=0.0.0" | ||
13 | |||
14 | // return the newest version that satisfies the provided constraint | ||
15 | func newest(versions []string, constraint string) (string, error) { | ||
16 | if constraint == "" { | ||
17 | constraint = anyVersion | ||
18 | } | ||
19 | cs, err := version.NewConstraint(constraint) | ||
20 | if err != nil { | ||
21 | return "", err | ||
22 | } | ||
23 | |||
24 | switch len(versions) { | ||
25 | case 0: | ||
26 | return "", errors.New("no versions found") | ||
27 | case 1: | ||
28 | v, err := version.NewVersion(versions[0]) | ||
29 | if err != nil { | ||
30 | return "", err | ||
31 | } | ||
32 | |||
33 | if !cs.Check(v) { | ||
34 | return "", fmt.Errorf("no version found matching constraint %q", constraint) | ||
35 | } | ||
36 | return versions[0], nil | ||
37 | } | ||
38 | |||
39 | sort.Slice(versions, func(i, j int) bool { | ||
40 | // versions should have already been validated | ||
41 | // sort invalid version strings to the end | ||
42 | iv, err := version.NewVersion(versions[i]) | ||
43 | if err != nil { | ||
44 | return true | ||
45 | } | ||
46 | jv, err := version.NewVersion(versions[j]) | ||
47 | if err != nil { | ||
48 | return true | ||
49 | } | ||
50 | return iv.GreaterThan(jv) | ||
51 | }) | ||
52 | |||
53 | // versions are now in order, so just find the first which satisfies the | ||
54 | // constraint | ||
55 | for i := range versions { | ||
56 | v, err := version.NewVersion(versions[i]) | ||
57 | if err != nil { | ||
58 | continue | ||
59 | } | ||
60 | if cs.Check(v) { | ||
61 | return versions[i], nil | ||
62 | } | ||
63 | } | ||
64 | |||
65 | return "", nil | ||
66 | } | ||
67 | |||
68 | // return the newest *moduleVersion that matches the given constraint | ||
69 | // TODO: reconcile these two types and newest* functions | ||
70 | func newestVersion(moduleVersions []*response.ModuleVersion, constraint string) (*response.ModuleVersion, error) { | ||
71 | var versions []string | ||
72 | modules := make(map[string]*response.ModuleVersion) | ||
73 | |||
74 | for _, m := range moduleVersions { | ||
75 | versions = append(versions, m.Version) | ||
76 | modules[m.Version] = m | ||
77 | } | ||
78 | |||
79 | match, err := newest(versions, constraint) | ||
80 | return modules[match], err | ||
81 | } | ||
82 | |||
83 | // return the newest moduleRecord that matches the given constraint | ||
84 | func newestRecord(moduleVersions []moduleRecord, constraint string) (moduleRecord, error) { | ||
85 | var versions []string | ||
86 | modules := make(map[string]moduleRecord) | ||
87 | |||
88 | for _, m := range moduleVersions { | ||
89 | versions = append(versions, m.Version) | ||
90 | modules[m.Version] = m | ||
91 | } | ||
92 | |||
93 | match, err := newest(versions, constraint) | ||
94 | return modules[match], err | ||
95 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/config/raw_config.go b/vendor/github.com/hashicorp/terraform/config/raw_config.go index f8498d8..1854a8b 100644 --- a/vendor/github.com/hashicorp/terraform/config/raw_config.go +++ b/vendor/github.com/hashicorp/terraform/config/raw_config.go | |||
@@ -3,8 +3,14 @@ package config | |||
3 | import ( | 3 | import ( |
4 | "bytes" | 4 | "bytes" |
5 | "encoding/gob" | 5 | "encoding/gob" |
6 | "errors" | ||
7 | "strconv" | ||
6 | "sync" | 8 | "sync" |
7 | 9 | ||
10 | "github.com/zclconf/go-cty/cty" | ||
11 | "github.com/zclconf/go-cty/cty/convert" | ||
12 | |||
13 | hcl2 "github.com/hashicorp/hcl2/hcl" | ||
8 | "github.com/hashicorp/hil" | 14 | "github.com/hashicorp/hil" |
9 | "github.com/hashicorp/hil/ast" | 15 | "github.com/hashicorp/hil/ast" |
10 | "github.com/mitchellh/copystructure" | 16 | "github.com/mitchellh/copystructure" |
@@ -27,8 +33,24 @@ const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66" | |||
27 | // RawConfig supports a query-like interface to request | 33 | // RawConfig supports a query-like interface to request |
28 | // information from deep within the structure. | 34 | // information from deep within the structure. |
29 | type RawConfig struct { | 35 | type RawConfig struct { |
30 | Key string | 36 | Key string |
31 | Raw map[string]interface{} | 37 | |
38 | // Only _one_ of Raw and Body may be populated at a time. | ||
39 | // | ||
40 | // In the normal case, Raw is populated and Body is nil. | ||
41 | // | ||
42 | // When the experimental HCL2 parsing mode is enabled, "Body" | ||
43 | // is populated and RawConfig serves only to transport the hcl2.Body | ||
44 | // through the rest of Terraform core so we can ultimately decode it | ||
45 | // once its schema is known. | ||
46 | // | ||
47 | // Once we transition to HCL2 as the primary representation, RawConfig | ||
48 | // should be removed altogether and the hcl2.Body should be passed | ||
49 | // around directly. | ||
50 | |||
51 | Raw map[string]interface{} | ||
52 | Body hcl2.Body | ||
53 | |||
32 | Interpolations []ast.Node | 54 | Interpolations []ast.Node |
33 | Variables map[string]InterpolatedVariable | 55 | Variables map[string]InterpolatedVariable |
34 | 56 | ||
@@ -48,6 +70,26 @@ func NewRawConfig(raw map[string]interface{}) (*RawConfig, error) { | |||
48 | return result, nil | 70 | return result, nil |
49 | } | 71 | } |
50 | 72 | ||
73 | // NewRawConfigHCL2 creates a new RawConfig that is serving as a capsule | ||
74 | // to transport a hcl2.Body. In this mode, the publicly-readable struct | ||
75 | // fields are not populated since all operations should instead be diverted | ||
76 | // to the HCL2 body. | ||
77 | // | ||
78 | // For a RawConfig object constructed with this function, the only valid use | ||
79 | // is to later retrieve the Body value and call its own methods. Callers | ||
80 | // may choose to set and then later handle the Key field, in a manner | ||
81 | // consistent with how it is handled by the Value method, but the Value | ||
82 | // method itself must not be used. | ||
83 | // | ||
84 | // This is an experimental codepath to be used only by the HCL2 config loader. | ||
85 | // Non-experimental parsing should _always_ use NewRawConfig to produce a | ||
86 | // fully-functional RawConfig object. | ||
87 | func NewRawConfigHCL2(body hcl2.Body) *RawConfig { | ||
88 | return &RawConfig{ | ||
89 | Body: body, | ||
90 | } | ||
91 | } | ||
92 | |||
51 | // RawMap returns a copy of the RawConfig.Raw map. | 93 | // RawMap returns a copy of the RawConfig.Raw map. |
52 | func (r *RawConfig) RawMap() map[string]interface{} { | 94 | func (r *RawConfig) RawMap() map[string]interface{} { |
53 | r.lock.Lock() | 95 | r.lock.Lock() |
@@ -69,6 +111,10 @@ func (r *RawConfig) Copy() *RawConfig { | |||
69 | r.lock.Lock() | 111 | r.lock.Lock() |
70 | defer r.lock.Unlock() | 112 | defer r.lock.Unlock() |
71 | 113 | ||
114 | if r.Body != nil { | ||
115 | return NewRawConfigHCL2(r.Body) | ||
116 | } | ||
117 | |||
72 | newRaw := make(map[string]interface{}) | 118 | newRaw := make(map[string]interface{}) |
73 | for k, v := range r.Raw { | 119 | for k, v := range r.Raw { |
74 | newRaw[k] = v | 120 | newRaw[k] = v |
@@ -223,6 +269,13 @@ func (r *RawConfig) init() error { | |||
223 | } | 269 | } |
224 | 270 | ||
225 | func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error { | 271 | func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error { |
272 | if r.Body != nil { | ||
273 | // For RawConfigs created for the HCL2 experiement, callers must | ||
274 | // use the HCL2 Body API directly rather than interpolating via | ||
275 | // the RawConfig. | ||
276 | return errors.New("this feature is not yet supported under the HCL2 experiment") | ||
277 | } | ||
278 | |||
226 | config, err := copystructure.Copy(r.Raw) | 279 | config, err := copystructure.Copy(r.Raw) |
227 | if err != nil { | 280 | if err != nil { |
228 | return err | 281 | return err |
@@ -268,6 +321,74 @@ func (r *RawConfig) merge(r2 *RawConfig) *RawConfig { | |||
268 | return result | 321 | return result |
269 | } | 322 | } |
270 | 323 | ||
324 | // couldBeInteger is a helper that determines if the represented value could | ||
325 | // result in an integer. | ||
326 | // | ||
327 | // This function only works for RawConfigs that have "Key" set, meaning that | ||
328 | // a single result can be produced. Calling this function will overwrite | ||
329 | // the Config and Value results to be a test value. | ||
330 | // | ||
331 | // This function is conservative. If there is some doubt about whether the | ||
332 | // result could be an integer -- for example, if it depends on a variable | ||
333 | // whose type we don't know yet -- it will still return true. | ||
334 | func (r *RawConfig) couldBeInteger() bool { | ||
335 | if r.Key == "" { | ||
336 | // un-keyed RawConfigs can never produce numbers | ||
337 | return false | ||
338 | } | ||
339 | if r.Body == nil { | ||
340 | // Normal path: using the interpolator in this package | ||
341 | // Interpolate with a fixed number to verify that its a number. | ||
342 | r.interpolate(func(root ast.Node) (interface{}, error) { | ||
343 | // Execute the node but transform the AST so that it returns | ||
344 | // a fixed value of "5" for all interpolations. | ||
345 | result, err := hil.Eval( | ||
346 | hil.FixedValueTransform( | ||
347 | root, &ast.LiteralNode{Value: "5", Typex: ast.TypeString}), | ||
348 | nil) | ||
349 | if err != nil { | ||
350 | return "", err | ||
351 | } | ||
352 | |||
353 | return result.Value, nil | ||
354 | }) | ||
355 | _, err := strconv.ParseInt(r.Value().(string), 0, 0) | ||
356 | return err == nil | ||
357 | } else { | ||
358 | // HCL2 experiment path: using the HCL2 API via shims | ||
359 | // | ||
360 | // This path catches fewer situations because we have to assume all | ||
361 | // variables are entirely unknown in HCL2, rather than the assumption | ||
362 | // above that all variables can be numbers because names like "var.foo" | ||
363 | // are considered a single variable rather than an attribute access. | ||
364 | // This is fine in practice, because we get a definitive answer | ||
365 | // during the graph walk when we have real values to work with. | ||
366 | attrs, diags := r.Body.JustAttributes() | ||
367 | if diags.HasErrors() { | ||
368 | // This body is not just a single attribute with a value, so | ||
369 | // this can't be a number. | ||
370 | return false | ||
371 | } | ||
372 | attr, hasAttr := attrs[r.Key] | ||
373 | if !hasAttr { | ||
374 | return false | ||
375 | } | ||
376 | result, diags := hcl2EvalWithUnknownVars(attr.Expr) | ||
377 | if diags.HasErrors() { | ||
378 | // We'll conservatively assume that this error is a result of | ||
379 | // us not being ready to fully-populate the scope, and catch | ||
380 | // any further problems during the main graph walk. | ||
381 | return true | ||
382 | } | ||
383 | |||
384 | // If the result is convertable to number then we'll allow it. | ||
385 | // We do this because an unknown string is optimistically convertable | ||
386 | // to number (might be "5") but a _known_ string "hello" is not. | ||
387 | _, err := convert.Convert(result, cty.Number) | ||
388 | return err == nil | ||
389 | } | ||
390 | } | ||
391 | |||
271 | // UnknownKeys returns the keys of the configuration that are unknown | 392 | // UnknownKeys returns the keys of the configuration that are unknown |
272 | // because they had interpolated variables that must be computed. | 393 | // because they had interpolated variables that must be computed. |
273 | func (r *RawConfig) UnknownKeys() []string { | 394 | func (r *RawConfig) UnknownKeys() []string { |
diff --git a/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go b/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go index ea68b4f..8a55e06 100644 --- a/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go +++ b/vendor/github.com/hashicorp/terraform/config/resource_mode_string.go | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | package config | 3 | package config |
4 | 4 | ||
5 | import "fmt" | 5 | import "strconv" |
6 | 6 | ||
7 | const _ResourceMode_name = "ManagedResourceModeDataResourceMode" | 7 | const _ResourceMode_name = "ManagedResourceModeDataResourceMode" |
8 | 8 | ||
@@ -10,7 +10,7 @@ var _ResourceMode_index = [...]uint8{0, 19, 35} | |||
10 | 10 | ||
11 | func (i ResourceMode) String() string { | 11 | func (i ResourceMode) String() string { |
12 | if i < 0 || i >= ResourceMode(len(_ResourceMode_index)-1) { | 12 | if i < 0 || i >= ResourceMode(len(_ResourceMode_index)-1) { |
13 | return fmt.Sprintf("ResourceMode(%d)", i) | 13 | return "ResourceMode(" + strconv.FormatInt(int64(i), 10) + ")" |
14 | } | 14 | } |
15 | return _ResourceMode_name[_ResourceMode_index[i]:_ResourceMode_index[i+1]] | 15 | return _ResourceMode_name[_ResourceMode_index[i]:_ResourceMode_index[i+1]] |
16 | } | 16 | } |
diff --git a/vendor/github.com/hashicorp/terraform/config/testing.go b/vendor/github.com/hashicorp/terraform/config/testing.go index f7bfadd..831fc77 100644 --- a/vendor/github.com/hashicorp/terraform/config/testing.go +++ b/vendor/github.com/hashicorp/terraform/config/testing.go | |||
@@ -6,6 +6,8 @@ import ( | |||
6 | 6 | ||
7 | // TestRawConfig is used to create a RawConfig for testing. | 7 | // TestRawConfig is used to create a RawConfig for testing. |
8 | func TestRawConfig(t *testing.T, c map[string]interface{}) *RawConfig { | 8 | func TestRawConfig(t *testing.T, c map[string]interface{}) *RawConfig { |
9 | t.Helper() | ||
10 | |||
9 | cfg, err := NewRawConfig(c) | 11 | cfg, err := NewRawConfig(c) |
10 | if err != nil { | 12 | if err != nil { |
11 | t.Fatalf("err: %s", err) | 13 | t.Fatalf("err: %s", err) |