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/config.go | |
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/config.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/config/config.go | 396 |
1 files changed, 261 insertions, 135 deletions
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 |