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 | |
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')
183 files changed, 9142 insertions, 3772 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) |
diff --git a/vendor/github.com/hashicorp/terraform/dag/dag.go b/vendor/github.com/hashicorp/terraform/dag/dag.go index f8776bc..b7eb10c 100644 --- a/vendor/github.com/hashicorp/terraform/dag/dag.go +++ b/vendor/github.com/hashicorp/terraform/dag/dag.go | |||
@@ -106,7 +106,7 @@ func (g *AcyclicGraph) TransitiveReduction() { | |||
106 | uTargets := g.DownEdges(u) | 106 | uTargets := g.DownEdges(u) |
107 | vs := AsVertexList(g.DownEdges(u)) | 107 | vs := AsVertexList(g.DownEdges(u)) |
108 | 108 | ||
109 | g.DepthFirstWalk(vs, func(v Vertex, d int) error { | 109 | g.depthFirstWalk(vs, false, func(v Vertex, d int) error { |
110 | shared := uTargets.Intersection(g.DownEdges(v)) | 110 | shared := uTargets.Intersection(g.DownEdges(v)) |
111 | for _, vPrime := range AsVertexList(shared) { | 111 | for _, vPrime := range AsVertexList(shared) { |
112 | g.RemoveEdge(BasicEdge(u, vPrime)) | 112 | g.RemoveEdge(BasicEdge(u, vPrime)) |
@@ -187,9 +187,18 @@ type vertexAtDepth struct { | |||
187 | } | 187 | } |
188 | 188 | ||
189 | // depthFirstWalk does a depth-first walk of the graph starting from | 189 | // depthFirstWalk does a depth-first walk of the graph starting from |
190 | // the vertices in start. This is not exported now but it would make sense | 190 | // the vertices in start. |
191 | // to export this publicly at some point. | ||
192 | func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error { | 191 | func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error { |
192 | return g.depthFirstWalk(start, true, f) | ||
193 | } | ||
194 | |||
195 | // This internal method provides the option of not sorting the vertices during | ||
196 | // the walk, which we use for the Transitive reduction. | ||
197 | // Some configurations can lead to fully-connected subgraphs, which makes our | ||
198 | // transitive reduction algorithm O(n^3). This is still passable for the size | ||
199 | // of our graphs, but the additional n^2 sort operations would make this | ||
200 | // uncomputable in a reasonable amount of time. | ||
201 | func (g *AcyclicGraph) depthFirstWalk(start []Vertex, sorted bool, f DepthWalkFunc) error { | ||
193 | defer g.debug.BeginOperation(typeDepthFirstWalk, "").End("") | 202 | defer g.debug.BeginOperation(typeDepthFirstWalk, "").End("") |
194 | 203 | ||
195 | seen := make(map[Vertex]struct{}) | 204 | seen := make(map[Vertex]struct{}) |
@@ -219,7 +228,11 @@ func (g *AcyclicGraph) DepthFirstWalk(start []Vertex, f DepthWalkFunc) error { | |||
219 | 228 | ||
220 | // Visit targets of this in a consistent order. | 229 | // Visit targets of this in a consistent order. |
221 | targets := AsVertexList(g.DownEdges(current.Vertex)) | 230 | targets := AsVertexList(g.DownEdges(current.Vertex)) |
222 | sort.Sort(byVertexName(targets)) | 231 | |
232 | if sorted { | ||
233 | sort.Sort(byVertexName(targets)) | ||
234 | } | ||
235 | |||
223 | for _, t := range targets { | 236 | for _, t := range targets { |
224 | frontier = append(frontier, &vertexAtDepth{ | 237 | frontier = append(frontier, &vertexAtDepth{ |
225 | Vertex: t, | 238 | Vertex: t, |
diff --git a/vendor/github.com/hashicorp/terraform/dag/marshal.go b/vendor/github.com/hashicorp/terraform/dag/marshal.go index 16d5dd6..c567d27 100644 --- a/vendor/github.com/hashicorp/terraform/dag/marshal.go +++ b/vendor/github.com/hashicorp/terraform/dag/marshal.go | |||
@@ -273,6 +273,9 @@ func (e *encoder) Encode(i interface{}) { | |||
273 | } | 273 | } |
274 | 274 | ||
275 | func (e *encoder) Add(v Vertex) { | 275 | func (e *encoder) Add(v Vertex) { |
276 | if e == nil { | ||
277 | return | ||
278 | } | ||
276 | e.Encode(marshalTransform{ | 279 | e.Encode(marshalTransform{ |
277 | Type: typeTransform, | 280 | Type: typeTransform, |
278 | AddVertex: newMarshalVertex(v), | 281 | AddVertex: newMarshalVertex(v), |
@@ -281,6 +284,9 @@ func (e *encoder) Add(v Vertex) { | |||
281 | 284 | ||
282 | // Remove records the removal of Vertex v. | 285 | // Remove records the removal of Vertex v. |
283 | func (e *encoder) Remove(v Vertex) { | 286 | func (e *encoder) Remove(v Vertex) { |
287 | if e == nil { | ||
288 | return | ||
289 | } | ||
284 | e.Encode(marshalTransform{ | 290 | e.Encode(marshalTransform{ |
285 | Type: typeTransform, | 291 | Type: typeTransform, |
286 | RemoveVertex: newMarshalVertex(v), | 292 | RemoveVertex: newMarshalVertex(v), |
@@ -288,6 +294,9 @@ func (e *encoder) Remove(v Vertex) { | |||
288 | } | 294 | } |
289 | 295 | ||
290 | func (e *encoder) Connect(edge Edge) { | 296 | func (e *encoder) Connect(edge Edge) { |
297 | if e == nil { | ||
298 | return | ||
299 | } | ||
291 | e.Encode(marshalTransform{ | 300 | e.Encode(marshalTransform{ |
292 | Type: typeTransform, | 301 | Type: typeTransform, |
293 | AddEdge: newMarshalEdge(edge), | 302 | AddEdge: newMarshalEdge(edge), |
@@ -295,6 +304,9 @@ func (e *encoder) Connect(edge Edge) { | |||
295 | } | 304 | } |
296 | 305 | ||
297 | func (e *encoder) RemoveEdge(edge Edge) { | 306 | func (e *encoder) RemoveEdge(edge Edge) { |
307 | if e == nil { | ||
308 | return | ||
309 | } | ||
298 | e.Encode(marshalTransform{ | 310 | e.Encode(marshalTransform{ |
299 | Type: typeTransform, | 311 | Type: typeTransform, |
300 | RemoveEdge: newMarshalEdge(edge), | 312 | RemoveEdge: newMarshalEdge(edge), |
diff --git a/vendor/github.com/hashicorp/terraform/dag/walk.go b/vendor/github.com/hashicorp/terraform/dag/walk.go index 23c87ad..f03b100 100644 --- a/vendor/github.com/hashicorp/terraform/dag/walk.go +++ b/vendor/github.com/hashicorp/terraform/dag/walk.go | |||
@@ -166,7 +166,7 @@ func (w *Walker) Update(g *AcyclicGraph) { | |||
166 | w.wait.Add(1) | 166 | w.wait.Add(1) |
167 | 167 | ||
168 | // Add to our own set so we know about it already | 168 | // Add to our own set so we know about it already |
169 | log.Printf("[DEBUG] dag/walk: added new vertex: %q", VertexName(v)) | 169 | log.Printf("[TRACE] dag/walk: added new vertex: %q", VertexName(v)) |
170 | w.vertices.Add(raw) | 170 | w.vertices.Add(raw) |
171 | 171 | ||
172 | // Initialize the vertex info | 172 | // Initialize the vertex info |
@@ -198,7 +198,7 @@ func (w *Walker) Update(g *AcyclicGraph) { | |||
198 | // Delete it out of the map | 198 | // Delete it out of the map |
199 | delete(w.vertexMap, v) | 199 | delete(w.vertexMap, v) |
200 | 200 | ||
201 | log.Printf("[DEBUG] dag/walk: removed vertex: %q", VertexName(v)) | 201 | log.Printf("[TRACE] dag/walk: removed vertex: %q", VertexName(v)) |
202 | w.vertices.Delete(raw) | 202 | w.vertices.Delete(raw) |
203 | } | 203 | } |
204 | 204 | ||
@@ -229,7 +229,7 @@ func (w *Walker) Update(g *AcyclicGraph) { | |||
229 | changedDeps.Add(waiter) | 229 | changedDeps.Add(waiter) |
230 | 230 | ||
231 | log.Printf( | 231 | log.Printf( |
232 | "[DEBUG] dag/walk: added edge: %q waiting on %q", | 232 | "[TRACE] dag/walk: added edge: %q waiting on %q", |
233 | VertexName(waiter), VertexName(dep)) | 233 | VertexName(waiter), VertexName(dep)) |
234 | w.edges.Add(raw) | 234 | w.edges.Add(raw) |
235 | } | 235 | } |
@@ -253,7 +253,7 @@ func (w *Walker) Update(g *AcyclicGraph) { | |||
253 | changedDeps.Add(waiter) | 253 | changedDeps.Add(waiter) |
254 | 254 | ||
255 | log.Printf( | 255 | log.Printf( |
256 | "[DEBUG] dag/walk: removed edge: %q waiting on %q", | 256 | "[TRACE] dag/walk: removed edge: %q waiting on %q", |
257 | VertexName(waiter), VertexName(dep)) | 257 | VertexName(waiter), VertexName(dep)) |
258 | w.edges.Delete(raw) | 258 | w.edges.Delete(raw) |
259 | } | 259 | } |
@@ -296,7 +296,7 @@ func (w *Walker) Update(g *AcyclicGraph) { | |||
296 | info.depsCancelCh = cancelCh | 296 | info.depsCancelCh = cancelCh |
297 | 297 | ||
298 | log.Printf( | 298 | log.Printf( |
299 | "[DEBUG] dag/walk: dependencies changed for %q, sending new deps", | 299 | "[TRACE] dag/walk: dependencies changed for %q, sending new deps", |
300 | VertexName(v)) | 300 | VertexName(v)) |
301 | 301 | ||
302 | // Start the waiter | 302 | // Start the waiter |
@@ -383,10 +383,10 @@ func (w *Walker) walkVertex(v Vertex, info *walkerVertex) { | |||
383 | // Run our callback or note that our upstream failed | 383 | // Run our callback or note that our upstream failed |
384 | var err error | 384 | var err error |
385 | if depsSuccess { | 385 | if depsSuccess { |
386 | log.Printf("[DEBUG] dag/walk: walking %q", VertexName(v)) | 386 | log.Printf("[TRACE] dag/walk: walking %q", VertexName(v)) |
387 | err = w.Callback(v) | 387 | err = w.Callback(v) |
388 | } else { | 388 | } else { |
389 | log.Printf("[DEBUG] dag/walk: upstream errored, not walking %q", VertexName(v)) | 389 | log.Printf("[TRACE] dag/walk: upstream errored, not walking %q", VertexName(v)) |
390 | err = errWalkUpstream | 390 | err = errWalkUpstream |
391 | } | 391 | } |
392 | 392 | ||
@@ -423,7 +423,7 @@ func (w *Walker) waitDeps( | |||
423 | return | 423 | return |
424 | 424 | ||
425 | case <-time.After(time.Second * 5): | 425 | case <-time.After(time.Second * 5): |
426 | log.Printf("[DEBUG] dag/walk: vertex %q, waiting for: %q", | 426 | log.Printf("[TRACE] dag/walk: vertex %q, waiting for: %q", |
427 | VertexName(v), VertexName(dep)) | 427 | VertexName(v), VertexName(dep)) |
428 | } | 428 | } |
429 | } | 429 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go b/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go deleted file mode 100644 index 18b8837..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/experiment/experiment.go +++ /dev/null | |||
@@ -1,154 +0,0 @@ | |||
1 | // experiment package contains helper functions for tracking experimental | ||
2 | // features throughout Terraform. | ||
3 | // | ||
4 | // This package should be used for creating, enabling, querying, and deleting | ||
5 | // experimental features. By unifying all of that onto a single interface, | ||
6 | // we can have the Go compiler help us by enforcing every place we touch | ||
7 | // an experimental feature. | ||
8 | // | ||
9 | // To create a new experiment: | ||
10 | // | ||
11 | // 1. Add the experiment to the global vars list below, prefixed with X_ | ||
12 | // | ||
13 | // 2. Add the experiment variable to the All listin the init() function | ||
14 | // | ||
15 | // 3. Use it! | ||
16 | // | ||
17 | // To remove an experiment: | ||
18 | // | ||
19 | // 1. Delete the experiment global var. | ||
20 | // | ||
21 | // 2. Try to compile and fix all the places where the var was referenced. | ||
22 | // | ||
23 | // To use an experiment: | ||
24 | // | ||
25 | // 1. Use Flag() if you want the experiment to be available from the CLI. | ||
26 | // | ||
27 | // 2. Use Enabled() to check whether it is enabled. | ||
28 | // | ||
29 | // As a general user: | ||
30 | // | ||
31 | // 1. The `-Xexperiment-name` flag | ||
32 | // 2. The `TF_X_<experiment-name>` env var. | ||
33 | // 3. The `TF_X_FORCE` env var can be set to force an experimental feature | ||
34 | // without human verifications. | ||
35 | // | ||
36 | package experiment | ||
37 | |||
38 | import ( | ||
39 | "flag" | ||
40 | "fmt" | ||
41 | "os" | ||
42 | "strconv" | ||
43 | "strings" | ||
44 | "sync" | ||
45 | ) | ||
46 | |||
47 | // The experiments that are available are listed below. Any package in | ||
48 | // Terraform defining an experiment should define the experiments below. | ||
49 | // By keeping them all within the experiment package we force a single point | ||
50 | // of definition and use. This allows the compiler to enforce references | ||
51 | // so it becomes easy to remove the features. | ||
52 | var ( | ||
53 | // Shadow graph. This is already on by default. Disabling it will be | ||
54 | // allowed for awhile in order for it to not block operations. | ||
55 | X_shadow = newBasicID("shadow", "SHADOW", false) | ||
56 | ) | ||
57 | |||
58 | // Global variables this package uses because we are a package | ||
59 | // with global state. | ||
60 | var ( | ||
61 | // all is the list of all experiements. Do not modify this. | ||
62 | All []ID | ||
63 | |||
64 | // enabled keeps track of what flags have been enabled | ||
65 | enabled map[string]bool | ||
66 | enabledLock sync.Mutex | ||
67 | |||
68 | // Hidden "experiment" that forces all others to be on without verification | ||
69 | x_force = newBasicID("force", "FORCE", false) | ||
70 | ) | ||
71 | |||
72 | func init() { | ||
73 | // The list of all experiments, update this when an experiment is added. | ||
74 | All = []ID{ | ||
75 | X_shadow, | ||
76 | x_force, | ||
77 | } | ||
78 | |||
79 | // Load | ||
80 | reload() | ||
81 | } | ||
82 | |||
83 | // reload is used by tests to reload the global state. This is called by | ||
84 | // init publicly. | ||
85 | func reload() { | ||
86 | // Initialize | ||
87 | enabledLock.Lock() | ||
88 | enabled = make(map[string]bool) | ||
89 | enabledLock.Unlock() | ||
90 | |||
91 | // Set defaults and check env vars | ||
92 | for _, id := range All { | ||
93 | // Get the default value | ||
94 | def := id.Default() | ||
95 | |||
96 | // If we set it in the env var, default it to true | ||
97 | key := fmt.Sprintf("TF_X_%s", strings.ToUpper(id.Env())) | ||
98 | if v := os.Getenv(key); v != "" { | ||
99 | def = v != "0" | ||
100 | } | ||
101 | |||
102 | // Set the default | ||
103 | SetEnabled(id, def) | ||
104 | } | ||
105 | } | ||
106 | |||
107 | // Enabled returns whether an experiment has been enabled or not. | ||
108 | func Enabled(id ID) bool { | ||
109 | enabledLock.Lock() | ||
110 | defer enabledLock.Unlock() | ||
111 | return enabled[id.Flag()] | ||
112 | } | ||
113 | |||
114 | // SetEnabled sets an experiment to enabled/disabled. Please check with | ||
115 | // the experiment docs for when calling this actually affects the experiment. | ||
116 | func SetEnabled(id ID, v bool) { | ||
117 | enabledLock.Lock() | ||
118 | defer enabledLock.Unlock() | ||
119 | enabled[id.Flag()] = v | ||
120 | } | ||
121 | |||
122 | // Force returns true if the -Xforce of TF_X_FORCE flag is present, which | ||
123 | // advises users of this package to not verify with the user that they want | ||
124 | // experimental behavior and to just continue with it. | ||
125 | func Force() bool { | ||
126 | return Enabled(x_force) | ||
127 | } | ||
128 | |||
129 | // Flag configures the given FlagSet with the flags to configure | ||
130 | // all active experiments. | ||
131 | func Flag(fs *flag.FlagSet) { | ||
132 | for _, id := range All { | ||
133 | desc := id.Flag() | ||
134 | key := fmt.Sprintf("X%s", id.Flag()) | ||
135 | fs.Var(&idValue{X: id}, key, desc) | ||
136 | } | ||
137 | } | ||
138 | |||
139 | // idValue implements flag.Value for setting the enabled/disabled state | ||
140 | // of an experiment from the CLI. | ||
141 | type idValue struct { | ||
142 | X ID | ||
143 | } | ||
144 | |||
145 | func (v *idValue) IsBoolFlag() bool { return true } | ||
146 | func (v *idValue) String() string { return strconv.FormatBool(Enabled(v.X)) } | ||
147 | func (v *idValue) Set(raw string) error { | ||
148 | b, err := strconv.ParseBool(raw) | ||
149 | if err == nil { | ||
150 | SetEnabled(v.X, b) | ||
151 | } | ||
152 | |||
153 | return err | ||
154 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/experiment/id.go b/vendor/github.com/hashicorp/terraform/helper/experiment/id.go deleted file mode 100644 index 8e2f707..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/experiment/id.go +++ /dev/null | |||
@@ -1,34 +0,0 @@ | |||
1 | package experiment | ||
2 | |||
3 | // ID represents an experimental feature. | ||
4 | // | ||
5 | // The global vars defined on this package should be used as ID values. | ||
6 | // This interface is purposely not implement-able outside of this package | ||
7 | // so that we can rely on the Go compiler to enforce all experiment references. | ||
8 | type ID interface { | ||
9 | Env() string | ||
10 | Flag() string | ||
11 | Default() bool | ||
12 | |||
13 | unexported() // So the ID can't be implemented externally. | ||
14 | } | ||
15 | |||
16 | // basicID implements ID. | ||
17 | type basicID struct { | ||
18 | EnvValue string | ||
19 | FlagValue string | ||
20 | DefaultValue bool | ||
21 | } | ||
22 | |||
23 | func newBasicID(flag, env string, def bool) ID { | ||
24 | return &basicID{ | ||
25 | EnvValue: env, | ||
26 | FlagValue: flag, | ||
27 | DefaultValue: def, | ||
28 | } | ||
29 | } | ||
30 | |||
31 | func (id *basicID) Env() string { return id.EnvValue } | ||
32 | func (id *basicID) Flag() string { return id.FlagValue } | ||
33 | func (id *basicID) Default() bool { return id.DefaultValue } | ||
34 | func (id *basicID) unexported() {} | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go b/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go index 64d8263..6ccc523 100644 --- a/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go +++ b/vendor/github.com/hashicorp/terraform/helper/hashcode/hashcode.go | |||
@@ -1,6 +1,8 @@ | |||
1 | package hashcode | 1 | package hashcode |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "bytes" | ||
5 | "fmt" | ||
4 | "hash/crc32" | 6 | "hash/crc32" |
5 | ) | 7 | ) |
6 | 8 | ||
@@ -20,3 +22,14 @@ func String(s string) int { | |||
20 | // v == MinInt | 22 | // v == MinInt |
21 | return 0 | 23 | return 0 |
22 | } | 24 | } |
25 | |||
26 | // Strings hashes a list of strings to a unique hashcode. | ||
27 | func Strings(strings []string) string { | ||
28 | var buf bytes.Buffer | ||
29 | |||
30 | for _, s := range strings { | ||
31 | buf.WriteString(fmt.Sprintf("%s-", s)) | ||
32 | } | ||
33 | |||
34 | return fmt.Sprintf("%d", String(buf.String())) | ||
35 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/logging/logging.go b/vendor/github.com/hashicorp/terraform/helper/logging/logging.go index 433cd77..6bd92f7 100644 --- a/vendor/github.com/hashicorp/terraform/helper/logging/logging.go +++ b/vendor/github.com/hashicorp/terraform/helper/logging/logging.go | |||
@@ -18,7 +18,7 @@ const ( | |||
18 | EnvLogFile = "TF_LOG_PATH" // Set to a file | 18 | EnvLogFile = "TF_LOG_PATH" // Set to a file |
19 | ) | 19 | ) |
20 | 20 | ||
21 | var validLevels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERROR"} | 21 | var ValidLevels = []logutils.LogLevel{"TRACE", "DEBUG", "INFO", "WARN", "ERROR"} |
22 | 22 | ||
23 | // LogOutput determines where we should send logs (if anywhere) and the log level. | 23 | // LogOutput determines where we should send logs (if anywhere) and the log level. |
24 | func LogOutput() (logOutput io.Writer, err error) { | 24 | func LogOutput() (logOutput io.Writer, err error) { |
@@ -40,7 +40,7 @@ func LogOutput() (logOutput io.Writer, err error) { | |||
40 | 40 | ||
41 | // This was the default since the beginning | 41 | // This was the default since the beginning |
42 | logOutput = &logutils.LevelFilter{ | 42 | logOutput = &logutils.LevelFilter{ |
43 | Levels: validLevels, | 43 | Levels: ValidLevels, |
44 | MinLevel: logutils.LogLevel(logLevel), | 44 | MinLevel: logutils.LogLevel(logLevel), |
45 | Writer: logOutput, | 45 | Writer: logOutput, |
46 | } | 46 | } |
@@ -77,7 +77,7 @@ func LogLevel() string { | |||
77 | logLevel = strings.ToUpper(envLevel) | 77 | logLevel = strings.ToUpper(envLevel) |
78 | } else { | 78 | } else { |
79 | log.Printf("[WARN] Invalid log level: %q. Defaulting to level: TRACE. Valid levels are: %+v", | 79 | log.Printf("[WARN] Invalid log level: %q. Defaulting to level: TRACE. Valid levels are: %+v", |
80 | envLevel, validLevels) | 80 | envLevel, ValidLevels) |
81 | } | 81 | } |
82 | 82 | ||
83 | return logLevel | 83 | return logLevel |
@@ -90,7 +90,7 @@ func IsDebugOrHigher() bool { | |||
90 | } | 90 | } |
91 | 91 | ||
92 | func isValidLogLevel(level string) bool { | 92 | func isValidLogLevel(level string) bool { |
93 | for _, l := range validLevels { | 93 | for _, l := range ValidLevels { |
94 | if strings.ToUpper(level) == string(l) { | 94 | if strings.ToUpper(level) == string(l) { |
95 | return true | 95 | return true |
96 | } | 96 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/logging/transport.go b/vendor/github.com/hashicorp/terraform/helper/logging/transport.go index 4477924..bddabe6 100644 --- a/vendor/github.com/hashicorp/terraform/helper/logging/transport.go +++ b/vendor/github.com/hashicorp/terraform/helper/logging/transport.go | |||
@@ -1,9 +1,12 @@ | |||
1 | package logging | 1 | package logging |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "bytes" | ||
5 | "encoding/json" | ||
4 | "log" | 6 | "log" |
5 | "net/http" | 7 | "net/http" |
6 | "net/http/httputil" | 8 | "net/http/httputil" |
9 | "strings" | ||
7 | ) | 10 | ) |
8 | 11 | ||
9 | type transport struct { | 12 | type transport struct { |
@@ -15,7 +18,7 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { | |||
15 | if IsDebugOrHigher() { | 18 | if IsDebugOrHigher() { |
16 | reqData, err := httputil.DumpRequestOut(req, true) | 19 | reqData, err := httputil.DumpRequestOut(req, true) |
17 | if err == nil { | 20 | if err == nil { |
18 | log.Printf("[DEBUG] "+logReqMsg, t.name, string(reqData)) | 21 | log.Printf("[DEBUG] "+logReqMsg, t.name, prettyPrintJsonLines(reqData)) |
19 | } else { | 22 | } else { |
20 | log.Printf("[ERROR] %s API Request error: %#v", t.name, err) | 23 | log.Printf("[ERROR] %s API Request error: %#v", t.name, err) |
21 | } | 24 | } |
@@ -29,7 +32,7 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) { | |||
29 | if IsDebugOrHigher() { | 32 | if IsDebugOrHigher() { |
30 | respData, err := httputil.DumpResponse(resp, true) | 33 | respData, err := httputil.DumpResponse(resp, true) |
31 | if err == nil { | 34 | if err == nil { |
32 | log.Printf("[DEBUG] "+logRespMsg, t.name, string(respData)) | 35 | log.Printf("[DEBUG] "+logRespMsg, t.name, prettyPrintJsonLines(respData)) |
33 | } else { | 36 | } else { |
34 | log.Printf("[ERROR] %s API Response error: %#v", t.name, err) | 37 | log.Printf("[ERROR] %s API Response error: %#v", t.name, err) |
35 | } | 38 | } |
@@ -42,6 +45,20 @@ func NewTransport(name string, t http.RoundTripper) *transport { | |||
42 | return &transport{name, t} | 45 | return &transport{name, t} |
43 | } | 46 | } |
44 | 47 | ||
48 | // prettyPrintJsonLines iterates through a []byte line-by-line, | ||
49 | // transforming any lines that are complete json into pretty-printed json. | ||
50 | func prettyPrintJsonLines(b []byte) string { | ||
51 | parts := strings.Split(string(b), "\n") | ||
52 | for i, p := range parts { | ||
53 | if b := []byte(p); json.Valid(b) { | ||
54 | var out bytes.Buffer | ||
55 | json.Indent(&out, b, "", " ") | ||
56 | parts[i] = out.String() | ||
57 | } | ||
58 | } | ||
59 | return strings.Join(parts, "\n") | ||
60 | } | ||
61 | |||
45 | const logReqMsg = `%s API Request Details: | 62 | const logReqMsg = `%s API Request Details: |
46 | ---[ REQUEST ]--------------------------------------- | 63 | ---[ REQUEST ]--------------------------------------- |
47 | %s | 64 | %s |
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/id.go b/vendor/github.com/hashicorp/terraform/helper/resource/id.go index 1cde67c..4494955 100644 --- a/vendor/github.com/hashicorp/terraform/helper/resource/id.go +++ b/vendor/github.com/hashicorp/terraform/helper/resource/id.go | |||
@@ -18,6 +18,11 @@ func UniqueId() string { | |||
18 | return PrefixedUniqueId(UniqueIdPrefix) | 18 | return PrefixedUniqueId(UniqueIdPrefix) |
19 | } | 19 | } |
20 | 20 | ||
21 | // UniqueIDSuffixLength is the string length of the suffix generated by | ||
22 | // PrefixedUniqueId. This can be used by length validation functions to | ||
23 | // ensure prefixes are the correct length for the target field. | ||
24 | const UniqueIDSuffixLength = 26 | ||
25 | |||
21 | // Helper for a resource to generate a unique identifier w/ given prefix | 26 | // Helper for a resource to generate a unique identifier w/ given prefix |
22 | // | 27 | // |
23 | // After the prefix, the ID consists of an incrementing 26 digit value (to match | 28 | // After the prefix, the ID consists of an incrementing 26 digit value (to match |
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/state.go b/vendor/github.com/hashicorp/terraform/helper/resource/state.go index 37c586a..c34e21b 100644 --- a/vendor/github.com/hashicorp/terraform/helper/resource/state.go +++ b/vendor/github.com/hashicorp/terraform/helper/resource/state.go | |||
@@ -46,7 +46,7 @@ type StateChangeConf struct { | |||
46 | // If the Timeout is exceeded before reaching the Target state, return an | 46 | // If the Timeout is exceeded before reaching the Target state, return an |
47 | // error. | 47 | // error. |
48 | // | 48 | // |
49 | // Otherwise, result the result of the first call to the Refresh function to | 49 | // Otherwise, the result is the result of the first call to the Refresh function to |
50 | // reach the target state. | 50 | // reach the target state. |
51 | func (conf *StateChangeConf) WaitForState() (interface{}, error) { | 51 | func (conf *StateChangeConf) WaitForState() (interface{}, error) { |
52 | log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target) | 52 | log.Printf("[DEBUG] Waiting for state to become: %s", conf.Target) |
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go index d7de1a0..b97673f 100644 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go +++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go | |||
@@ -11,11 +11,13 @@ import ( | |||
11 | "reflect" | 11 | "reflect" |
12 | "regexp" | 12 | "regexp" |
13 | "strings" | 13 | "strings" |
14 | "syscall" | ||
14 | "testing" | 15 | "testing" |
15 | 16 | ||
16 | "github.com/davecgh/go-spew/spew" | 17 | "github.com/davecgh/go-spew/spew" |
17 | "github.com/hashicorp/go-getter" | 18 | "github.com/hashicorp/errwrap" |
18 | "github.com/hashicorp/go-multierror" | 19 | "github.com/hashicorp/go-multierror" |
20 | "github.com/hashicorp/logutils" | ||
19 | "github.com/hashicorp/terraform/config/module" | 21 | "github.com/hashicorp/terraform/config/module" |
20 | "github.com/hashicorp/terraform/helper/logging" | 22 | "github.com/hashicorp/terraform/helper/logging" |
21 | "github.com/hashicorp/terraform/terraform" | 23 | "github.com/hashicorp/terraform/terraform" |
@@ -186,6 +188,10 @@ type TestCheckFunc func(*terraform.State) error | |||
186 | // ImportStateCheckFunc is the check function for ImportState tests | 188 | // ImportStateCheckFunc is the check function for ImportState tests |
187 | type ImportStateCheckFunc func([]*terraform.InstanceState) error | 189 | type ImportStateCheckFunc func([]*terraform.InstanceState) error |
188 | 190 | ||
191 | // ImportStateIdFunc is an ID generation function to help with complex ID | ||
192 | // generation for ImportState tests. | ||
193 | type ImportStateIdFunc func(*terraform.State) (string, error) | ||
194 | |||
189 | // TestCase is a single acceptance test case used to test the apply/destroy | 195 | // TestCase is a single acceptance test case used to test the apply/destroy |
190 | // lifecycle of a resource in a specific configuration. | 196 | // lifecycle of a resource in a specific configuration. |
191 | // | 197 | // |
@@ -260,6 +266,15 @@ type TestStep struct { | |||
260 | // below. | 266 | // below. |
261 | PreConfig func() | 267 | PreConfig func() |
262 | 268 | ||
269 | // Taint is a list of resource addresses to taint prior to the execution of | ||
270 | // the step. Be sure to only include this at a step where the referenced | ||
271 | // address will be present in state, as it will fail the test if the resource | ||
272 | // is missing. | ||
273 | // | ||
274 | // This option is ignored on ImportState tests, and currently only works for | ||
275 | // resources in the root module path. | ||
276 | Taint []string | ||
277 | |||
263 | //--------------------------------------------------------------- | 278 | //--------------------------------------------------------------- |
264 | // Test modes. One of the following groups of settings must be | 279 | // Test modes. One of the following groups of settings must be |
265 | // set to determine what the test step will do. Ideally we would've | 280 | // set to determine what the test step will do. Ideally we would've |
@@ -304,10 +319,19 @@ type TestStep struct { | |||
304 | // no-op plans | 319 | // no-op plans |
305 | PlanOnly bool | 320 | PlanOnly bool |
306 | 321 | ||
322 | // PreventDiskCleanup can be set to true for testing terraform modules which | ||
323 | // require access to disk at runtime. Note that this will leave files in the | ||
324 | // temp folder | ||
325 | PreventDiskCleanup bool | ||
326 | |||
307 | // PreventPostDestroyRefresh can be set to true for cases where data sources | 327 | // PreventPostDestroyRefresh can be set to true for cases where data sources |
308 | // are tested alongside real resources | 328 | // are tested alongside real resources |
309 | PreventPostDestroyRefresh bool | 329 | PreventPostDestroyRefresh bool |
310 | 330 | ||
331 | // SkipFunc is called before applying config, but after PreConfig | ||
332 | // This is useful for defining test steps with platform-dependent checks | ||
333 | SkipFunc func() (bool, error) | ||
334 | |||
311 | //--------------------------------------------------------------- | 335 | //--------------------------------------------------------------- |
312 | // ImportState testing | 336 | // ImportState testing |
313 | //--------------------------------------------------------------- | 337 | //--------------------------------------------------------------- |
@@ -329,6 +353,12 @@ type TestStep struct { | |||
329 | // the unset ImportStateId field. | 353 | // the unset ImportStateId field. |
330 | ImportStateIdPrefix string | 354 | ImportStateIdPrefix string |
331 | 355 | ||
356 | // ImportStateIdFunc is a function that can be used to dynamically generate | ||
357 | // the ID for the ImportState tests. It is sent the state, which can be | ||
358 | // checked to derive the attributes necessary and generate the string in the | ||
359 | // desired format. | ||
360 | ImportStateIdFunc ImportStateIdFunc | ||
361 | |||
332 | // ImportStateCheck checks the results of ImportState. It should be | 362 | // ImportStateCheck checks the results of ImportState. It should be |
333 | // used to verify that the resulting value of ImportState has the | 363 | // used to verify that the resulting value of ImportState has the |
334 | // proper resources, IDs, and attributes. | 364 | // proper resources, IDs, and attributes. |
@@ -345,6 +375,60 @@ type TestStep struct { | |||
345 | ImportStateVerifyIgnore []string | 375 | ImportStateVerifyIgnore []string |
346 | } | 376 | } |
347 | 377 | ||
378 | // Set to a file mask in sprintf format where %s is test name | ||
379 | const EnvLogPathMask = "TF_LOG_PATH_MASK" | ||
380 | |||
381 | func LogOutput(t TestT) (logOutput io.Writer, err error) { | ||
382 | logOutput = ioutil.Discard | ||
383 | |||
384 | logLevel := logging.LogLevel() | ||
385 | if logLevel == "" { | ||
386 | return | ||
387 | } | ||
388 | |||
389 | logOutput = os.Stderr | ||
390 | |||
391 | if logPath := os.Getenv(logging.EnvLogFile); logPath != "" { | ||
392 | var err error | ||
393 | logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666) | ||
394 | if err != nil { | ||
395 | return nil, err | ||
396 | } | ||
397 | } | ||
398 | |||
399 | if logPathMask := os.Getenv(EnvLogPathMask); logPathMask != "" { | ||
400 | // Escape special characters which may appear if we have subtests | ||
401 | testName := strings.Replace(t.Name(), "/", "__", -1) | ||
402 | |||
403 | logPath := fmt.Sprintf(logPathMask, testName) | ||
404 | var err error | ||
405 | logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666) | ||
406 | if err != nil { | ||
407 | return nil, err | ||
408 | } | ||
409 | } | ||
410 | |||
411 | // This was the default since the beginning | ||
412 | logOutput = &logutils.LevelFilter{ | ||
413 | Levels: logging.ValidLevels, | ||
414 | MinLevel: logutils.LogLevel(logLevel), | ||
415 | Writer: logOutput, | ||
416 | } | ||
417 | |||
418 | return | ||
419 | } | ||
420 | |||
421 | // ParallelTest performs an acceptance test on a resource, allowing concurrency | ||
422 | // with other ParallelTest. | ||
423 | // | ||
424 | // Tests will fail if they do not properly handle conditions to allow multiple | ||
425 | // tests to occur against the same resource or service (e.g. random naming). | ||
426 | // All other requirements of the Test function also apply to this function. | ||
427 | func ParallelTest(t TestT, c TestCase) { | ||
428 | t.Parallel() | ||
429 | Test(t, c) | ||
430 | } | ||
431 | |||
348 | // Test performs an acceptance test on a resource. | 432 | // Test performs an acceptance test on a resource. |
349 | // | 433 | // |
350 | // Tests are not run unless an environmental variable "TF_ACC" is | 434 | // Tests are not run unless an environmental variable "TF_ACC" is |
@@ -366,7 +450,7 @@ func Test(t TestT, c TestCase) { | |||
366 | return | 450 | return |
367 | } | 451 | } |
368 | 452 | ||
369 | logWriter, err := logging.LogOutput() | 453 | logWriter, err := LogOutput(t) |
370 | if err != nil { | 454 | if err != nil { |
371 | t.Error(fmt.Errorf("error setting up logging: %s", err)) | 455 | t.Error(fmt.Errorf("error setting up logging: %s", err)) |
372 | } | 456 | } |
@@ -398,7 +482,18 @@ func Test(t TestT, c TestCase) { | |||
398 | errored := false | 482 | errored := false |
399 | for i, step := range c.Steps { | 483 | for i, step := range c.Steps { |
400 | var err error | 484 | var err error |
401 | log.Printf("[WARN] Test: Executing step %d", i) | 485 | log.Printf("[DEBUG] Test: Executing step %d", i) |
486 | |||
487 | if step.SkipFunc != nil { | ||
488 | skip, err := step.SkipFunc() | ||
489 | if err != nil { | ||
490 | t.Fatal(err) | ||
491 | } | ||
492 | if skip { | ||
493 | log.Printf("[WARN] Skipping step %d", i) | ||
494 | continue | ||
495 | } | ||
496 | } | ||
402 | 497 | ||
403 | if step.Config == "" && !step.ImportState { | 498 | if step.Config == "" && !step.ImportState { |
404 | err = fmt.Errorf( | 499 | err = fmt.Errorf( |
@@ -418,6 +513,15 @@ func Test(t TestT, c TestCase) { | |||
418 | } | 513 | } |
419 | } | 514 | } |
420 | 515 | ||
516 | // If we expected an error, but did not get one, fail | ||
517 | if err == nil && step.ExpectError != nil { | ||
518 | errored = true | ||
519 | t.Error(fmt.Sprintf( | ||
520 | "Step %d, no error received, but expected a match to:\n\n%s\n\n", | ||
521 | i, step.ExpectError)) | ||
522 | break | ||
523 | } | ||
524 | |||
421 | // If there was an error, exit | 525 | // If there was an error, exit |
422 | if err != nil { | 526 | if err != nil { |
423 | // Perhaps we expected an error? Check if it matches | 527 | // Perhaps we expected an error? Check if it matches |
@@ -485,6 +589,7 @@ func Test(t TestT, c TestCase) { | |||
485 | Config: lastStep.Config, | 589 | Config: lastStep.Config, |
486 | Check: c.CheckDestroy, | 590 | Check: c.CheckDestroy, |
487 | Destroy: true, | 591 | Destroy: true, |
592 | PreventDiskCleanup: lastStep.PreventDiskCleanup, | ||
488 | PreventPostDestroyRefresh: c.PreventPostDestroyRefresh, | 593 | PreventPostDestroyRefresh: c.PreventPostDestroyRefresh, |
489 | } | 594 | } |
490 | 595 | ||
@@ -593,18 +698,12 @@ func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r | |||
593 | if err != nil { | 698 | if err != nil { |
594 | return err | 699 | return err |
595 | } | 700 | } |
596 | if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { | 701 | if diags := ctx.Validate(); len(diags) > 0 { |
597 | if len(es) > 0 { | 702 | if diags.HasErrors() { |
598 | estrs := make([]string, len(es)) | 703 | return errwrap.Wrapf("config is invalid: {{err}}", diags.Err()) |
599 | for i, e := range es { | ||
600 | estrs[i] = e.Error() | ||
601 | } | ||
602 | return fmt.Errorf( | ||
603 | "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v", | ||
604 | ws, estrs) | ||
605 | } | 704 | } |
606 | 705 | ||
607 | log.Printf("[WARN] Config warnings: %#v", ws) | 706 | log.Printf("[WARN] Config warnings:\n%s", diags.Err().Error()) |
608 | } | 707 | } |
609 | 708 | ||
610 | // Refresh! | 709 | // Refresh! |
@@ -657,9 +756,7 @@ func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r | |||
657 | return nil | 756 | return nil |
658 | } | 757 | } |
659 | 758 | ||
660 | func testModule( | 759 | func testModule(opts terraform.ContextOpts, step TestStep) (*module.Tree, error) { |
661 | opts terraform.ContextOpts, | ||
662 | step TestStep) (*module.Tree, error) { | ||
663 | if step.PreConfig != nil { | 760 | if step.PreConfig != nil { |
664 | step.PreConfig() | 761 | step.PreConfig() |
665 | } | 762 | } |
@@ -669,7 +766,12 @@ func testModule( | |||
669 | return nil, fmt.Errorf( | 766 | return nil, fmt.Errorf( |
670 | "Error creating temporary directory for config: %s", err) | 767 | "Error creating temporary directory for config: %s", err) |
671 | } | 768 | } |
672 | defer os.RemoveAll(cfgPath) | 769 | |
770 | if step.PreventDiskCleanup { | ||
771 | log.Printf("[INFO] Skipping defer os.RemoveAll call") | ||
772 | } else { | ||
773 | defer os.RemoveAll(cfgPath) | ||
774 | } | ||
673 | 775 | ||
674 | // Write the configuration | 776 | // Write the configuration |
675 | cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf")) | 777 | cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf")) |
@@ -693,10 +795,11 @@ func testModule( | |||
693 | } | 795 | } |
694 | 796 | ||
695 | // Load the modules | 797 | // Load the modules |
696 | modStorage := &getter.FolderStorage{ | 798 | modStorage := &module.Storage{ |
697 | StorageDir: filepath.Join(cfgPath, ".tfmodules"), | 799 | StorageDir: filepath.Join(cfgPath, ".tfmodules"), |
800 | Mode: module.GetModeGet, | ||
698 | } | 801 | } |
699 | err = mod.Load(modStorage, module.GetModeGet) | 802 | err = mod.Load(modStorage) |
700 | if err != nil { | 803 | if err != nil { |
701 | return nil, fmt.Errorf("Error downloading modules: %s", err) | 804 | return nil, fmt.Errorf("Error downloading modules: %s", err) |
702 | } | 805 | } |
@@ -771,12 +874,29 @@ func TestCheckResourceAttrSet(name, key string) TestCheckFunc { | |||
771 | return err | 874 | return err |
772 | } | 875 | } |
773 | 876 | ||
774 | if val, ok := is.Attributes[key]; ok && val != "" { | 877 | return testCheckResourceAttrSet(is, name, key) |
775 | return nil | 878 | } |
879 | } | ||
880 | |||
881 | // TestCheckModuleResourceAttrSet - as per TestCheckResourceAttrSet but with | ||
882 | // support for non-root modules | ||
883 | func TestCheckModuleResourceAttrSet(mp []string, name string, key string) TestCheckFunc { | ||
884 | return func(s *terraform.State) error { | ||
885 | is, err := modulePathPrimaryInstanceState(s, mp, name) | ||
886 | if err != nil { | ||
887 | return err | ||
776 | } | 888 | } |
777 | 889 | ||
890 | return testCheckResourceAttrSet(is, name, key) | ||
891 | } | ||
892 | } | ||
893 | |||
894 | func testCheckResourceAttrSet(is *terraform.InstanceState, name string, key string) error { | ||
895 | if val, ok := is.Attributes[key]; !ok || val == "" { | ||
778 | return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key) | 896 | return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key) |
779 | } | 897 | } |
898 | |||
899 | return nil | ||
780 | } | 900 | } |
781 | 901 | ||
782 | // TestCheckResourceAttr is a TestCheckFunc which validates | 902 | // TestCheckResourceAttr is a TestCheckFunc which validates |
@@ -788,21 +908,37 @@ func TestCheckResourceAttr(name, key, value string) TestCheckFunc { | |||
788 | return err | 908 | return err |
789 | } | 909 | } |
790 | 910 | ||
791 | if v, ok := is.Attributes[key]; !ok || v != value { | 911 | return testCheckResourceAttr(is, name, key, value) |
792 | if !ok { | 912 | } |
793 | return fmt.Errorf("%s: Attribute '%s' not found", name, key) | 913 | } |
794 | } | ||
795 | 914 | ||
796 | return fmt.Errorf( | 915 | // TestCheckModuleResourceAttr - as per TestCheckResourceAttr but with |
797 | "%s: Attribute '%s' expected %#v, got %#v", | 916 | // support for non-root modules |
798 | name, | 917 | func TestCheckModuleResourceAttr(mp []string, name string, key string, value string) TestCheckFunc { |
799 | key, | 918 | return func(s *terraform.State) error { |
800 | value, | 919 | is, err := modulePathPrimaryInstanceState(s, mp, name) |
801 | v) | 920 | if err != nil { |
921 | return err | ||
802 | } | 922 | } |
803 | 923 | ||
804 | return nil | 924 | return testCheckResourceAttr(is, name, key, value) |
925 | } | ||
926 | } | ||
927 | |||
928 | func testCheckResourceAttr(is *terraform.InstanceState, name string, key string, value string) error { | ||
929 | if v, ok := is.Attributes[key]; !ok || v != value { | ||
930 | if !ok { | ||
931 | return fmt.Errorf("%s: Attribute '%s' not found", name, key) | ||
932 | } | ||
933 | |||
934 | return fmt.Errorf( | ||
935 | "%s: Attribute '%s' expected %#v, got %#v", | ||
936 | name, | ||
937 | key, | ||
938 | value, | ||
939 | v) | ||
805 | } | 940 | } |
941 | return nil | ||
806 | } | 942 | } |
807 | 943 | ||
808 | // TestCheckNoResourceAttr is a TestCheckFunc which ensures that | 944 | // TestCheckNoResourceAttr is a TestCheckFunc which ensures that |
@@ -814,14 +950,31 @@ func TestCheckNoResourceAttr(name, key string) TestCheckFunc { | |||
814 | return err | 950 | return err |
815 | } | 951 | } |
816 | 952 | ||
817 | if _, ok := is.Attributes[key]; ok { | 953 | return testCheckNoResourceAttr(is, name, key) |
818 | return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key) | 954 | } |
955 | } | ||
956 | |||
957 | // TestCheckModuleNoResourceAttr - as per TestCheckNoResourceAttr but with | ||
958 | // support for non-root modules | ||
959 | func TestCheckModuleNoResourceAttr(mp []string, name string, key string) TestCheckFunc { | ||
960 | return func(s *terraform.State) error { | ||
961 | is, err := modulePathPrimaryInstanceState(s, mp, name) | ||
962 | if err != nil { | ||
963 | return err | ||
819 | } | 964 | } |
820 | 965 | ||
821 | return nil | 966 | return testCheckNoResourceAttr(is, name, key) |
822 | } | 967 | } |
823 | } | 968 | } |
824 | 969 | ||
970 | func testCheckNoResourceAttr(is *terraform.InstanceState, name string, key string) error { | ||
971 | if _, ok := is.Attributes[key]; ok { | ||
972 | return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key) | ||
973 | } | ||
974 | |||
975 | return nil | ||
976 | } | ||
977 | |||
825 | // TestMatchResourceAttr is a TestCheckFunc which checks that the value | 978 | // TestMatchResourceAttr is a TestCheckFunc which checks that the value |
826 | // in state for the given name/key combination matches the given regex. | 979 | // in state for the given name/key combination matches the given regex. |
827 | func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { | 980 | func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { |
@@ -831,17 +984,34 @@ func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { | |||
831 | return err | 984 | return err |
832 | } | 985 | } |
833 | 986 | ||
834 | if !r.MatchString(is.Attributes[key]) { | 987 | return testMatchResourceAttr(is, name, key, r) |
835 | return fmt.Errorf( | 988 | } |
836 | "%s: Attribute '%s' didn't match %q, got %#v", | 989 | } |
837 | name, | 990 | |
838 | key, | 991 | // TestModuleMatchResourceAttr - as per TestMatchResourceAttr but with |
839 | r.String(), | 992 | // support for non-root modules |
840 | is.Attributes[key]) | 993 | func TestModuleMatchResourceAttr(mp []string, name string, key string, r *regexp.Regexp) TestCheckFunc { |
994 | return func(s *terraform.State) error { | ||
995 | is, err := modulePathPrimaryInstanceState(s, mp, name) | ||
996 | if err != nil { | ||
997 | return err | ||
841 | } | 998 | } |
842 | 999 | ||
843 | return nil | 1000 | return testMatchResourceAttr(is, name, key, r) |
1001 | } | ||
1002 | } | ||
1003 | |||
1004 | func testMatchResourceAttr(is *terraform.InstanceState, name string, key string, r *regexp.Regexp) error { | ||
1005 | if !r.MatchString(is.Attributes[key]) { | ||
1006 | return fmt.Errorf( | ||
1007 | "%s: Attribute '%s' didn't match %q, got %#v", | ||
1008 | name, | ||
1009 | key, | ||
1010 | r.String(), | ||
1011 | is.Attributes[key]) | ||
844 | } | 1012 | } |
1013 | |||
1014 | return nil | ||
845 | } | 1015 | } |
846 | 1016 | ||
847 | // TestCheckResourceAttrPtr is like TestCheckResourceAttr except the | 1017 | // TestCheckResourceAttrPtr is like TestCheckResourceAttr except the |
@@ -853,6 +1023,14 @@ func TestCheckResourceAttrPtr(name string, key string, value *string) TestCheckF | |||
853 | } | 1023 | } |
854 | } | 1024 | } |
855 | 1025 | ||
1026 | // TestCheckModuleResourceAttrPtr - as per TestCheckResourceAttrPtr but with | ||
1027 | // support for non-root modules | ||
1028 | func TestCheckModuleResourceAttrPtr(mp []string, name string, key string, value *string) TestCheckFunc { | ||
1029 | return func(s *terraform.State) error { | ||
1030 | return TestCheckModuleResourceAttr(mp, name, key, *value)(s) | ||
1031 | } | ||
1032 | } | ||
1033 | |||
856 | // TestCheckResourceAttrPair is a TestCheckFunc which validates that the values | 1034 | // TestCheckResourceAttrPair is a TestCheckFunc which validates that the values |
857 | // in state for a pair of name/key combinations are equal. | 1035 | // in state for a pair of name/key combinations are equal. |
858 | func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { | 1036 | func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { |
@@ -861,33 +1039,57 @@ func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string | |||
861 | if err != nil { | 1039 | if err != nil { |
862 | return err | 1040 | return err |
863 | } | 1041 | } |
864 | vFirst, ok := isFirst.Attributes[keyFirst] | ||
865 | if !ok { | ||
866 | return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst) | ||
867 | } | ||
868 | 1042 | ||
869 | isSecond, err := primaryInstanceState(s, nameSecond) | 1043 | isSecond, err := primaryInstanceState(s, nameSecond) |
870 | if err != nil { | 1044 | if err != nil { |
871 | return err | 1045 | return err |
872 | } | 1046 | } |
873 | vSecond, ok := isSecond.Attributes[keySecond] | 1047 | |
874 | if !ok { | 1048 | return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond) |
875 | return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond) | 1049 | } |
1050 | } | ||
1051 | |||
1052 | // TestCheckModuleResourceAttrPair - as per TestCheckResourceAttrPair but with | ||
1053 | // support for non-root modules | ||
1054 | func TestCheckModuleResourceAttrPair(mpFirst []string, nameFirst string, keyFirst string, mpSecond []string, nameSecond string, keySecond string) TestCheckFunc { | ||
1055 | return func(s *terraform.State) error { | ||
1056 | isFirst, err := modulePathPrimaryInstanceState(s, mpFirst, nameFirst) | ||
1057 | if err != nil { | ||
1058 | return err | ||
876 | } | 1059 | } |
877 | 1060 | ||
878 | if vFirst != vSecond { | 1061 | isSecond, err := modulePathPrimaryInstanceState(s, mpSecond, nameSecond) |
879 | return fmt.Errorf( | 1062 | if err != nil { |
880 | "%s: Attribute '%s' expected %#v, got %#v", | 1063 | return err |
881 | nameFirst, | ||
882 | keyFirst, | ||
883 | vSecond, | ||
884 | vFirst) | ||
885 | } | 1064 | } |
886 | 1065 | ||
887 | return nil | 1066 | return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond) |
888 | } | 1067 | } |
889 | } | 1068 | } |
890 | 1069 | ||
1070 | func testCheckResourceAttrPair(isFirst *terraform.InstanceState, nameFirst string, keyFirst string, isSecond *terraform.InstanceState, nameSecond string, keySecond string) error { | ||
1071 | vFirst, ok := isFirst.Attributes[keyFirst] | ||
1072 | if !ok { | ||
1073 | return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst) | ||
1074 | } | ||
1075 | |||
1076 | vSecond, ok := isSecond.Attributes[keySecond] | ||
1077 | if !ok { | ||
1078 | return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond) | ||
1079 | } | ||
1080 | |||
1081 | if vFirst != vSecond { | ||
1082 | return fmt.Errorf( | ||
1083 | "%s: Attribute '%s' expected %#v, got %#v", | ||
1084 | nameFirst, | ||
1085 | keyFirst, | ||
1086 | vSecond, | ||
1087 | vFirst) | ||
1088 | } | ||
1089 | |||
1090 | return nil | ||
1091 | } | ||
1092 | |||
891 | // TestCheckOutput checks an output in the Terraform configuration | 1093 | // TestCheckOutput checks an output in the Terraform configuration |
892 | func TestCheckOutput(name, value string) TestCheckFunc { | 1094 | func TestCheckOutput(name, value string) TestCheckFunc { |
893 | return func(s *terraform.State) error { | 1095 | return func(s *terraform.State) error { |
@@ -936,23 +1138,43 @@ type TestT interface { | |||
936 | Error(args ...interface{}) | 1138 | Error(args ...interface{}) |
937 | Fatal(args ...interface{}) | 1139 | Fatal(args ...interface{}) |
938 | Skip(args ...interface{}) | 1140 | Skip(args ...interface{}) |
1141 | Name() string | ||
1142 | Parallel() | ||
939 | } | 1143 | } |
940 | 1144 | ||
941 | // This is set to true by unit tests to alter some behavior | 1145 | // This is set to true by unit tests to alter some behavior |
942 | var testTesting = false | 1146 | var testTesting = false |
943 | 1147 | ||
944 | // primaryInstanceState returns the primary instance state for the given resource name. | 1148 | // modulePrimaryInstanceState returns the instance state for the given resource |
945 | func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) { | 1149 | // name in a ModuleState |
946 | ms := s.RootModule() | 1150 | func modulePrimaryInstanceState(s *terraform.State, ms *terraform.ModuleState, name string) (*terraform.InstanceState, error) { |
947 | rs, ok := ms.Resources[name] | 1151 | rs, ok := ms.Resources[name] |
948 | if !ok { | 1152 | if !ok { |
949 | return nil, fmt.Errorf("Not found: %s", name) | 1153 | return nil, fmt.Errorf("Not found: %s in %s", name, ms.Path) |
950 | } | 1154 | } |
951 | 1155 | ||
952 | is := rs.Primary | 1156 | is := rs.Primary |
953 | if is == nil { | 1157 | if is == nil { |
954 | return nil, fmt.Errorf("No primary instance: %s", name) | 1158 | return nil, fmt.Errorf("No primary instance: %s in %s", name, ms.Path) |
955 | } | 1159 | } |
956 | 1160 | ||
957 | return is, nil | 1161 | return is, nil |
958 | } | 1162 | } |
1163 | |||
1164 | // modulePathPrimaryInstanceState returns the primary instance state for the | ||
1165 | // given resource name in a given module path. | ||
1166 | func modulePathPrimaryInstanceState(s *terraform.State, mp []string, name string) (*terraform.InstanceState, error) { | ||
1167 | ms := s.ModuleByPath(mp) | ||
1168 | if ms == nil { | ||
1169 | return nil, fmt.Errorf("No module found at: %s", mp) | ||
1170 | } | ||
1171 | |||
1172 | return modulePrimaryInstanceState(s, ms, name) | ||
1173 | } | ||
1174 | |||
1175 | // primaryInstanceState returns the primary instance state for the given | ||
1176 | // resource name in the root module. | ||
1177 | func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) { | ||
1178 | ms := s.RootModule() | ||
1179 | return modulePrimaryInstanceState(s, ms, name) | ||
1180 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go index 537a11c..033f126 100644 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go +++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go | |||
@@ -1,10 +1,12 @@ | |||
1 | package resource | 1 | package resource |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "errors" | ||
4 | "fmt" | 5 | "fmt" |
5 | "log" | 6 | "log" |
6 | "strings" | 7 | "strings" |
7 | 8 | ||
9 | "github.com/hashicorp/errwrap" | ||
8 | "github.com/hashicorp/terraform/terraform" | 10 | "github.com/hashicorp/terraform/terraform" |
9 | ) | 11 | ) |
10 | 12 | ||
@@ -20,6 +22,14 @@ func testStep( | |||
20 | opts terraform.ContextOpts, | 22 | opts terraform.ContextOpts, |
21 | state *terraform.State, | 23 | state *terraform.State, |
22 | step TestStep) (*terraform.State, error) { | 24 | step TestStep) (*terraform.State, error) { |
25 | // Pre-taint any resources that have been defined in Taint, as long as this | ||
26 | // is not a destroy step. | ||
27 | if !step.Destroy { | ||
28 | if err := testStepTaint(state, step); err != nil { | ||
29 | return state, err | ||
30 | } | ||
31 | } | ||
32 | |||
23 | mod, err := testModule(opts, step) | 33 | mod, err := testModule(opts, step) |
24 | if err != nil { | 34 | if err != nil { |
25 | return state, err | 35 | return state, err |
@@ -33,17 +43,12 @@ func testStep( | |||
33 | if err != nil { | 43 | if err != nil { |
34 | return state, fmt.Errorf("Error initializing context: %s", err) | 44 | return state, fmt.Errorf("Error initializing context: %s", err) |
35 | } | 45 | } |
36 | if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { | 46 | if diags := ctx.Validate(); len(diags) > 0 { |
37 | if len(es) > 0 { | 47 | if diags.HasErrors() { |
38 | estrs := make([]string, len(es)) | 48 | return nil, errwrap.Wrapf("config is invalid: {{err}}", diags.Err()) |
39 | for i, e := range es { | ||
40 | estrs[i] = e.Error() | ||
41 | } | ||
42 | return state, fmt.Errorf( | ||
43 | "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v", | ||
44 | ws, estrs) | ||
45 | } | 49 | } |
46 | log.Printf("[WARN] Config warnings: %#v", ws) | 50 | |
51 | log.Printf("[WARN] Config warnings:\n%s", diags) | ||
47 | } | 52 | } |
48 | 53 | ||
49 | // Refresh! | 54 | // Refresh! |
@@ -158,3 +163,19 @@ func testStep( | |||
158 | // Made it here? Good job test step! | 163 | // Made it here? Good job test step! |
159 | return state, nil | 164 | return state, nil |
160 | } | 165 | } |
166 | |||
167 | func testStepTaint(state *terraform.State, step TestStep) error { | ||
168 | for _, p := range step.Taint { | ||
169 | m := state.RootModule() | ||
170 | if m == nil { | ||
171 | return errors.New("no state") | ||
172 | } | ||
173 | rs, ok := m.Resources[p] | ||
174 | if !ok { | ||
175 | return fmt.Errorf("resource %q not found in state", p) | ||
176 | } | ||
177 | log.Printf("[WARN] Test: Explicitly tainting resource %q", p) | ||
178 | rs.Taint() | ||
179 | } | ||
180 | return nil | ||
181 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go index 28ad105..94fef3c 100644 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go +++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing_import_state.go | |||
@@ -16,15 +16,24 @@ func testStepImportState( | |||
16 | state *terraform.State, | 16 | state *terraform.State, |
17 | step TestStep) (*terraform.State, error) { | 17 | step TestStep) (*terraform.State, error) { |
18 | // Determine the ID to import | 18 | // Determine the ID to import |
19 | importId := step.ImportStateId | 19 | var importId string |
20 | if importId == "" { | 20 | switch { |
21 | case step.ImportStateIdFunc != nil: | ||
22 | var err error | ||
23 | importId, err = step.ImportStateIdFunc(state) | ||
24 | if err != nil { | ||
25 | return state, err | ||
26 | } | ||
27 | case step.ImportStateId != "": | ||
28 | importId = step.ImportStateId | ||
29 | default: | ||
21 | resource, err := testResource(step, state) | 30 | resource, err := testResource(step, state) |
22 | if err != nil { | 31 | if err != nil { |
23 | return state, err | 32 | return state, err |
24 | } | 33 | } |
25 | |||
26 | importId = resource.Primary.ID | 34 | importId = resource.Primary.ID |
27 | } | 35 | } |
36 | |||
28 | importPrefix := step.ImportStateIdPrefix | 37 | importPrefix := step.ImportStateIdPrefix |
29 | if importPrefix != "" { | 38 | if importPrefix != "" { |
30 | importId = fmt.Sprintf("%s%s", importPrefix, importId) | 39 | importId = fmt.Sprintf("%s%s", importPrefix, importId) |
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/wait.go b/vendor/github.com/hashicorp/terraform/helper/resource/wait.go index ca50e29..e56a515 100644 --- a/vendor/github.com/hashicorp/terraform/helper/resource/wait.go +++ b/vendor/github.com/hashicorp/terraform/helper/resource/wait.go | |||
@@ -74,7 +74,7 @@ func RetryableError(err error) *RetryError { | |||
74 | return &RetryError{Err: err, Retryable: true} | 74 | return &RetryError{Err: err, Retryable: true} |
75 | } | 75 | } |
76 | 76 | ||
77 | // NonRetryableError is a helper to create a RetryError that's _not)_ retryable | 77 | // NonRetryableError is a helper to create a RetryError that's _not_ retryable |
78 | // from a given error. | 78 | // from a given error. |
79 | func NonRetryableError(err error) *RetryError { | 79 | func NonRetryableError(err error) *RetryError { |
80 | if err == nil { | 80 | if err == nil { |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go index a0729c0..57fbba7 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/backend.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/backend.go | |||
@@ -65,7 +65,7 @@ func (b *Backend) Configure(c *terraform.ResourceConfig) error { | |||
65 | 65 | ||
66 | // Get a ResourceData for this configuration. To do this, we actually | 66 | // Get a ResourceData for this configuration. To do this, we actually |
67 | // generate an intermediary "diff" although that is never exposed. | 67 | // generate an intermediary "diff" although that is never exposed. |
68 | diff, err := sm.Diff(nil, c) | 68 | diff, err := sm.Diff(nil, c, nil, nil) |
69 | if err != nil { | 69 | if err != nil { |
70 | return err | 70 | return err |
71 | } | 71 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go new file mode 100644 index 0000000..bf952f6 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/schema/core_schema.go | |||
@@ -0,0 +1,155 @@ | |||
1 | package schema | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/terraform/config/configschema" | ||
7 | "github.com/zclconf/go-cty/cty" | ||
8 | ) | ||
9 | |||
10 | // The functions and methods in this file are concerned with the conversion | ||
11 | // of this package's schema model into the slightly-lower-level schema model | ||
12 | // used by Terraform core for configuration parsing. | ||
13 | |||
14 | // CoreConfigSchema lowers the receiver to the schema model expected by | ||
15 | // Terraform core. | ||
16 | // | ||
17 | // This lower-level model has fewer features than the schema in this package, | ||
18 | // describing only the basic structure of configuration and state values we | ||
19 | // expect. The full schemaMap from this package is still required for full | ||
20 | // validation, handling of default values, etc. | ||
21 | // | ||
22 | // This method presumes a schema that passes InternalValidate, and so may | ||
23 | // panic or produce an invalid result if given an invalid schemaMap. | ||
24 | func (m schemaMap) CoreConfigSchema() *configschema.Block { | ||
25 | if len(m) == 0 { | ||
26 | // We return an actual (empty) object here, rather than a nil, | ||
27 | // because a nil result would mean that we don't have a schema at | ||
28 | // all, rather than that we have an empty one. | ||
29 | return &configschema.Block{} | ||
30 | } | ||
31 | |||
32 | ret := &configschema.Block{ | ||
33 | Attributes: map[string]*configschema.Attribute{}, | ||
34 | BlockTypes: map[string]*configschema.NestedBlock{}, | ||
35 | } | ||
36 | |||
37 | for name, schema := range m { | ||
38 | if schema.Elem == nil { | ||
39 | ret.Attributes[name] = schema.coreConfigSchemaAttribute() | ||
40 | continue | ||
41 | } | ||
42 | switch schema.Elem.(type) { | ||
43 | case *Schema: | ||
44 | ret.Attributes[name] = schema.coreConfigSchemaAttribute() | ||
45 | case *Resource: | ||
46 | ret.BlockTypes[name] = schema.coreConfigSchemaBlock() | ||
47 | default: | ||
48 | // Should never happen for a valid schema | ||
49 | panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", schema.Elem)) | ||
50 | } | ||
51 | } | ||
52 | |||
53 | return ret | ||
54 | } | ||
55 | |||
56 | // coreConfigSchemaAttribute prepares a configschema.Attribute representation | ||
57 | // of a schema. This is appropriate only for primitives or collections whose | ||
58 | // Elem is an instance of Schema. Use coreConfigSchemaBlock for collections | ||
59 | // whose elem is a whole resource. | ||
60 | func (s *Schema) coreConfigSchemaAttribute() *configschema.Attribute { | ||
61 | return &configschema.Attribute{ | ||
62 | Type: s.coreConfigSchemaType(), | ||
63 | Optional: s.Optional, | ||
64 | Required: s.Required, | ||
65 | Computed: s.Computed, | ||
66 | Sensitive: s.Sensitive, | ||
67 | } | ||
68 | } | ||
69 | |||
70 | // coreConfigSchemaBlock prepares a configschema.NestedBlock representation of | ||
71 | // a schema. This is appropriate only for collections whose Elem is an instance | ||
72 | // of Resource, and will panic otherwise. | ||
73 | func (s *Schema) coreConfigSchemaBlock() *configschema.NestedBlock { | ||
74 | ret := &configschema.NestedBlock{} | ||
75 | if nested := s.Elem.(*Resource).CoreConfigSchema(); nested != nil { | ||
76 | ret.Block = *nested | ||
77 | } | ||
78 | switch s.Type { | ||
79 | case TypeList: | ||
80 | ret.Nesting = configschema.NestingList | ||
81 | case TypeSet: | ||
82 | ret.Nesting = configschema.NestingSet | ||
83 | case TypeMap: | ||
84 | ret.Nesting = configschema.NestingMap | ||
85 | default: | ||
86 | // Should never happen for a valid schema | ||
87 | panic(fmt.Errorf("invalid s.Type %s for s.Elem being resource", s.Type)) | ||
88 | } | ||
89 | |||
90 | ret.MinItems = s.MinItems | ||
91 | ret.MaxItems = s.MaxItems | ||
92 | |||
93 | if s.Required && s.MinItems == 0 { | ||
94 | // configschema doesn't have a "required" representation for nested | ||
95 | // blocks, but we can fake it by requiring at least one item. | ||
96 | ret.MinItems = 1 | ||
97 | } | ||
98 | |||
99 | return ret | ||
100 | } | ||
101 | |||
102 | // coreConfigSchemaType determines the core config schema type that corresponds | ||
103 | // to a particular schema's type. | ||
104 | func (s *Schema) coreConfigSchemaType() cty.Type { | ||
105 | switch s.Type { | ||
106 | case TypeString: | ||
107 | return cty.String | ||
108 | case TypeBool: | ||
109 | return cty.Bool | ||
110 | case TypeInt, TypeFloat: | ||
111 | // configschema doesn't distinguish int and float, so helper/schema | ||
112 | // will deal with this as an additional validation step after | ||
113 | // configuration has been parsed and decoded. | ||
114 | return cty.Number | ||
115 | case TypeList, TypeSet, TypeMap: | ||
116 | var elemType cty.Type | ||
117 | switch set := s.Elem.(type) { | ||
118 | case *Schema: | ||
119 | elemType = set.coreConfigSchemaType() | ||
120 | case *Resource: | ||
121 | // In practice we don't actually use this for normal schema | ||
122 | // construction because we construct a NestedBlock in that | ||
123 | // case instead. See schemaMap.CoreConfigSchema. | ||
124 | elemType = set.CoreConfigSchema().ImpliedType() | ||
125 | default: | ||
126 | if set != nil { | ||
127 | // Should never happen for a valid schema | ||
128 | panic(fmt.Errorf("invalid Schema.Elem %#v; need *Schema or *Resource", s.Elem)) | ||
129 | } | ||
130 | // Some pre-existing schemas assume string as default, so we need | ||
131 | // to be compatible with them. | ||
132 | elemType = cty.String | ||
133 | } | ||
134 | switch s.Type { | ||
135 | case TypeList: | ||
136 | return cty.List(elemType) | ||
137 | case TypeSet: | ||
138 | return cty.Set(elemType) | ||
139 | case TypeMap: | ||
140 | return cty.Map(elemType) | ||
141 | default: | ||
142 | // can never get here in practice, due to the case we're inside | ||
143 | panic("invalid collection type") | ||
144 | } | ||
145 | default: | ||
146 | // should never happen for a valid schema | ||
147 | panic(fmt.Errorf("invalid Schema.Type %s", s.Type)) | ||
148 | } | ||
149 | } | ||
150 | |||
151 | // CoreConfigSchema is a convenient shortcut for calling CoreConfigSchema | ||
152 | // on the resource's schema. | ||
153 | func (r *Resource) CoreConfigSchema() *configschema.Block { | ||
154 | return schemaMap(r.Schema).CoreConfigSchema() | ||
155 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go b/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go index 5a03d2d..8d93750 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/data_source_resource_shim.go | |||
@@ -32,7 +32,7 @@ func DataSourceResourceShim(name string, dataSource *Resource) *Resource { | |||
32 | 32 | ||
33 | // FIXME: Link to some further docs either on the website or in the | 33 | // FIXME: Link to some further docs either on the website or in the |
34 | // changelog, once such a thing exists. | 34 | // changelog, once such a thing exists. |
35 | dataSource.deprecationMessage = fmt.Sprintf( | 35 | dataSource.DeprecationMessage = fmt.Sprintf( |
36 | "using %s as a resource is deprecated; consider using the data source instead", | 36 | "using %s as a resource is deprecated; consider using the data source instead", |
37 | name, | 37 | name, |
38 | ) | 38 | ) |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go index 1660a67..b80b223 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader.go | |||
@@ -126,6 +126,8 @@ func addrToSchema(addr []string, schemaMap map[string]*Schema) []*Schema { | |||
126 | switch v := current.Elem.(type) { | 126 | switch v := current.Elem.(type) { |
127 | case ValueType: | 127 | case ValueType: |
128 | current = &Schema{Type: v} | 128 | current = &Schema{Type: v} |
129 | case *Schema: | ||
130 | current, _ = current.Elem.(*Schema) | ||
129 | default: | 131 | default: |
130 | // maps default to string values. This is all we can have | 132 | // maps default to string values. This is all we can have |
131 | // if this is nested in another list or map. | 133 | // if this is nested in another list or map. |
@@ -249,11 +251,10 @@ func readObjectField( | |||
249 | } | 251 | } |
250 | 252 | ||
251 | // convert map values to the proper primitive type based on schema.Elem | 253 | // convert map values to the proper primitive type based on schema.Elem |
252 | func mapValuesToPrimitive(m map[string]interface{}, schema *Schema) error { | 254 | func mapValuesToPrimitive(k string, m map[string]interface{}, schema *Schema) error { |
253 | 255 | elemType, err := getValueType(k, schema) | |
254 | elemType := TypeString | 256 | if err != nil { |
255 | if et, ok := schema.Elem.(ValueType); ok { | 257 | return err |
256 | elemType = et | ||
257 | } | 258 | } |
258 | 259 | ||
259 | switch elemType { | 260 | switch elemType { |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go index f958bbc..55a301d 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go | |||
@@ -206,7 +206,7 @@ func (r *ConfigFieldReader) readMap(k string, schema *Schema) (FieldReadResult, | |||
206 | panic(fmt.Sprintf("unknown type: %#v", mraw)) | 206 | panic(fmt.Sprintf("unknown type: %#v", mraw)) |
207 | } | 207 | } |
208 | 208 | ||
209 | err := mapValuesToPrimitive(result, schema) | 209 | err := mapValuesToPrimitive(k, result, schema) |
210 | if err != nil { | 210 | if err != nil { |
211 | return FieldReadResult{}, nil | 211 | return FieldReadResult{}, nil |
212 | } | 212 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go index 16bbae2..d558a5b 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go | |||
@@ -29,29 +29,59 @@ type DiffFieldReader struct { | |||
29 | Diff *terraform.InstanceDiff | 29 | Diff *terraform.InstanceDiff |
30 | Source FieldReader | 30 | Source FieldReader |
31 | Schema map[string]*Schema | 31 | Schema map[string]*Schema |
32 | |||
33 | // cache for memoizing ReadField calls. | ||
34 | cache map[string]cachedFieldReadResult | ||
35 | } | ||
36 | |||
37 | type cachedFieldReadResult struct { | ||
38 | val FieldReadResult | ||
39 | err error | ||
32 | } | 40 | } |
33 | 41 | ||
34 | func (r *DiffFieldReader) ReadField(address []string) (FieldReadResult, error) { | 42 | func (r *DiffFieldReader) ReadField(address []string) (FieldReadResult, error) { |
43 | if r.cache == nil { | ||
44 | r.cache = make(map[string]cachedFieldReadResult) | ||
45 | } | ||
46 | |||
47 | // Create the cache key by joining around a value that isn't a valid part | ||
48 | // of an address. This assumes that the Source and Schema are not changed | ||
49 | // for the life of this DiffFieldReader. | ||
50 | cacheKey := strings.Join(address, "|") | ||
51 | if cached, ok := r.cache[cacheKey]; ok { | ||
52 | return cached.val, cached.err | ||
53 | } | ||
54 | |||
35 | schemaList := addrToSchema(address, r.Schema) | 55 | schemaList := addrToSchema(address, r.Schema) |
36 | if len(schemaList) == 0 { | 56 | if len(schemaList) == 0 { |
57 | r.cache[cacheKey] = cachedFieldReadResult{} | ||
37 | return FieldReadResult{}, nil | 58 | return FieldReadResult{}, nil |
38 | } | 59 | } |
39 | 60 | ||
61 | var res FieldReadResult | ||
62 | var err error | ||
63 | |||
40 | schema := schemaList[len(schemaList)-1] | 64 | schema := schemaList[len(schemaList)-1] |
41 | switch schema.Type { | 65 | switch schema.Type { |
42 | case TypeBool, TypeInt, TypeFloat, TypeString: | 66 | case TypeBool, TypeInt, TypeFloat, TypeString: |
43 | return r.readPrimitive(address, schema) | 67 | res, err = r.readPrimitive(address, schema) |
44 | case TypeList: | 68 | case TypeList: |
45 | return readListField(r, address, schema) | 69 | res, err = readListField(r, address, schema) |
46 | case TypeMap: | 70 | case TypeMap: |
47 | return r.readMap(address, schema) | 71 | res, err = r.readMap(address, schema) |
48 | case TypeSet: | 72 | case TypeSet: |
49 | return r.readSet(address, schema) | 73 | res, err = r.readSet(address, schema) |
50 | case typeObject: | 74 | case typeObject: |
51 | return readObjectField(r, address, schema.Elem.(map[string]*Schema)) | 75 | res, err = readObjectField(r, address, schema.Elem.(map[string]*Schema)) |
52 | default: | 76 | default: |
53 | panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) | 77 | panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) |
54 | } | 78 | } |
79 | |||
80 | r.cache[cacheKey] = cachedFieldReadResult{ | ||
81 | val: res, | ||
82 | err: err, | ||
83 | } | ||
84 | return res, err | ||
55 | } | 85 | } |
56 | 86 | ||
57 | func (r *DiffFieldReader) readMap( | 87 | func (r *DiffFieldReader) readMap( |
@@ -92,7 +122,8 @@ func (r *DiffFieldReader) readMap( | |||
92 | result[k] = v.New | 122 | result[k] = v.New |
93 | } | 123 | } |
94 | 124 | ||
95 | err = mapValuesToPrimitive(result, schema) | 125 | key := address[len(address)-1] |
126 | err = mapValuesToPrimitive(key, result, schema) | ||
96 | if err != nil { | 127 | if err != nil { |
97 | return FieldReadResult{}, nil | 128 | return FieldReadResult{}, nil |
98 | } | 129 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go index 9533981..054efe0 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_reader_map.go | |||
@@ -61,7 +61,7 @@ func (r *MapFieldReader) readMap(k string, schema *Schema) (FieldReadResult, err | |||
61 | return true | 61 | return true |
62 | }) | 62 | }) |
63 | 63 | ||
64 | err := mapValuesToPrimitive(result, schema) | 64 | err := mapValuesToPrimitive(k, result, schema) |
65 | if err != nil { | 65 | if err != nil { |
66 | return FieldReadResult{}, nil | 66 | return FieldReadResult{}, nil |
67 | } | 67 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go index 689ed8d..814c7ba 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/field_writer_map.go | |||
@@ -39,6 +39,19 @@ func (w *MapFieldWriter) unsafeWriteField(addr string, value string) { | |||
39 | w.result[addr] = value | 39 | w.result[addr] = value |
40 | } | 40 | } |
41 | 41 | ||
42 | // clearTree clears a field and any sub-fields of the given address out of the | ||
43 | // map. This should be used to reset some kind of complex structures (namely | ||
44 | // sets) before writing to make sure that any conflicting data is removed (for | ||
45 | // example, if the set was previously written to the writer's layer). | ||
46 | func (w *MapFieldWriter) clearTree(addr []string) { | ||
47 | prefix := strings.Join(addr, ".") + "." | ||
48 | for k := range w.result { | ||
49 | if strings.HasPrefix(k, prefix) { | ||
50 | delete(w.result, k) | ||
51 | } | ||
52 | } | ||
53 | } | ||
54 | |||
42 | func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error { | 55 | func (w *MapFieldWriter) WriteField(addr []string, value interface{}) error { |
43 | w.lock.Lock() | 56 | w.lock.Lock() |
44 | defer w.lock.Unlock() | 57 | defer w.lock.Unlock() |
@@ -115,6 +128,14 @@ func (w *MapFieldWriter) setList( | |||
115 | return fmt.Errorf("%s: %s", k, err) | 128 | return fmt.Errorf("%s: %s", k, err) |
116 | } | 129 | } |
117 | 130 | ||
131 | // Wipe the set from the current writer prior to writing if it exists. | ||
132 | // Multiple writes to the same layer is a lot safer for lists than sets due | ||
133 | // to the fact that indexes are always deterministic and the length will | ||
134 | // always be updated with the current length on the last write, but making | ||
135 | // sure we have a clean namespace removes any chance for edge cases to pop up | ||
136 | // and ensures that the last write to the set is the correct value. | ||
137 | w.clearTree(addr) | ||
138 | |||
118 | // Set the entire list. | 139 | // Set the entire list. |
119 | var err error | 140 | var err error |
120 | for i, elem := range vs { | 141 | for i, elem := range vs { |
@@ -162,6 +183,10 @@ func (w *MapFieldWriter) setMap( | |||
162 | vs[mk.String()] = mv.Interface() | 183 | vs[mk.String()] = mv.Interface() |
163 | } | 184 | } |
164 | 185 | ||
186 | // Wipe this address tree. The contents of the map should always reflect the | ||
187 | // last write made to it. | ||
188 | w.clearTree(addr) | ||
189 | |||
165 | // Remove the pure key since we're setting the full map value | 190 | // Remove the pure key since we're setting the full map value |
166 | delete(w.result, k) | 191 | delete(w.result, k) |
167 | 192 | ||
@@ -308,6 +333,13 @@ func (w *MapFieldWriter) setSet( | |||
308 | value = s | 333 | value = s |
309 | } | 334 | } |
310 | 335 | ||
336 | // Clear any keys that match the set address first. This is necessary because | ||
337 | // it's always possible and sometimes may be necessary to write to a certain | ||
338 | // writer layer more than once with different set data each time, which will | ||
339 | // lead to different keys being inserted, which can lead to determinism | ||
340 | // problems when the old data isn't wiped first. | ||
341 | w.clearTree(addr) | ||
342 | |||
311 | for code, elem := range value.(*Set).m { | 343 | for code, elem := range value.(*Set).m { |
312 | if err := w.set(append(addrCopy, code), elem); err != nil { | 344 | if err := w.set(append(addrCopy, code), elem); err != nil { |
313 | return err | 345 | return err |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go b/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go index 3a97629..38cd8c7 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/getsource_string.go | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | package schema | 3 | package schema |
4 | 4 | ||
5 | import "fmt" | 5 | import "strconv" |
6 | 6 | ||
7 | const ( | 7 | const ( |
8 | _getSource_name_0 = "getSourceStategetSourceConfig" | 8 | _getSource_name_0 = "getSourceStategetSourceConfig" |
@@ -13,8 +13,6 @@ const ( | |||
13 | 13 | ||
14 | var ( | 14 | var ( |
15 | _getSource_index_0 = [...]uint8{0, 14, 29} | 15 | _getSource_index_0 = [...]uint8{0, 14, 29} |
16 | _getSource_index_1 = [...]uint8{0, 13} | ||
17 | _getSource_index_2 = [...]uint8{0, 12} | ||
18 | _getSource_index_3 = [...]uint8{0, 18, 32} | 16 | _getSource_index_3 = [...]uint8{0, 18, 32} |
19 | ) | 17 | ) |
20 | 18 | ||
@@ -31,6 +29,6 @@ func (i getSource) String() string { | |||
31 | i -= 15 | 29 | i -= 15 |
32 | return _getSource_name_3[_getSource_index_3[i]:_getSource_index_3[i+1]] | 30 | return _getSource_name_3[_getSource_index_3[i]:_getSource_index_3[i+1]] |
33 | default: | 31 | default: |
34 | return fmt.Sprintf("getSource(%d)", i) | 32 | return "getSource(" + strconv.FormatInt(int64(i), 10) + ")" |
35 | } | 33 | } |
36 | } | 34 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go index fb28b41..6cd325d 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provider.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/provider.go | |||
@@ -9,6 +9,7 @@ import ( | |||
9 | 9 | ||
10 | "github.com/hashicorp/go-multierror" | 10 | "github.com/hashicorp/go-multierror" |
11 | "github.com/hashicorp/terraform/config" | 11 | "github.com/hashicorp/terraform/config" |
12 | "github.com/hashicorp/terraform/config/configschema" | ||
12 | "github.com/hashicorp/terraform/terraform" | 13 | "github.com/hashicorp/terraform/terraform" |
13 | ) | 14 | ) |
14 | 15 | ||
@@ -58,7 +59,7 @@ type Provider struct { | |||
58 | 59 | ||
59 | meta interface{} | 60 | meta interface{} |
60 | 61 | ||
61 | // a mutex is required because TestReset can directly repalce the stopCtx | 62 | // a mutex is required because TestReset can directly replace the stopCtx |
62 | stopMu sync.Mutex | 63 | stopMu sync.Mutex |
63 | stopCtx context.Context | 64 | stopCtx context.Context |
64 | stopCtxCancel context.CancelFunc | 65 | stopCtxCancel context.CancelFunc |
@@ -185,6 +186,29 @@ func (p *Provider) TestReset() error { | |||
185 | return nil | 186 | return nil |
186 | } | 187 | } |
187 | 188 | ||
189 | // GetSchema implementation of terraform.ResourceProvider interface | ||
190 | func (p *Provider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) { | ||
191 | resourceTypes := map[string]*configschema.Block{} | ||
192 | dataSources := map[string]*configschema.Block{} | ||
193 | |||
194 | for _, name := range req.ResourceTypes { | ||
195 | if r, exists := p.ResourcesMap[name]; exists { | ||
196 | resourceTypes[name] = r.CoreConfigSchema() | ||
197 | } | ||
198 | } | ||
199 | for _, name := range req.DataSources { | ||
200 | if r, exists := p.DataSourcesMap[name]; exists { | ||
201 | dataSources[name] = r.CoreConfigSchema() | ||
202 | } | ||
203 | } | ||
204 | |||
205 | return &terraform.ProviderSchema{ | ||
206 | Provider: schemaMap(p.Schema).CoreConfigSchema(), | ||
207 | ResourceTypes: resourceTypes, | ||
208 | DataSources: dataSources, | ||
209 | }, nil | ||
210 | } | ||
211 | |||
188 | // Input implementation of terraform.ResourceProvider interface. | 212 | // Input implementation of terraform.ResourceProvider interface. |
189 | func (p *Provider) Input( | 213 | func (p *Provider) Input( |
190 | input terraform.UIInput, | 214 | input terraform.UIInput, |
@@ -227,7 +251,7 @@ func (p *Provider) Configure(c *terraform.ResourceConfig) error { | |||
227 | 251 | ||
228 | // Get a ResourceData for this configuration. To do this, we actually | 252 | // Get a ResourceData for this configuration. To do this, we actually |
229 | // generate an intermediary "diff" although that is never exposed. | 253 | // generate an intermediary "diff" although that is never exposed. |
230 | diff, err := sm.Diff(nil, c) | 254 | diff, err := sm.Diff(nil, c, nil, p.meta) |
231 | if err != nil { | 255 | if err != nil { |
232 | return err | 256 | return err |
233 | } | 257 | } |
@@ -269,7 +293,7 @@ func (p *Provider) Diff( | |||
269 | return nil, fmt.Errorf("unknown resource type: %s", info.Type) | 293 | return nil, fmt.Errorf("unknown resource type: %s", info.Type) |
270 | } | 294 | } |
271 | 295 | ||
272 | return r.Diff(s, c) | 296 | return r.Diff(s, c, p.meta) |
273 | } | 297 | } |
274 | 298 | ||
275 | // Refresh implementation of terraform.ResourceProvider interface. | 299 | // Refresh implementation of terraform.ResourceProvider interface. |
@@ -305,6 +329,10 @@ func (p *Provider) Resources() []terraform.ResourceType { | |||
305 | result = append(result, terraform.ResourceType{ | 329 | result = append(result, terraform.ResourceType{ |
306 | Name: k, | 330 | Name: k, |
307 | Importable: resource.Importer != nil, | 331 | Importable: resource.Importer != nil, |
332 | |||
333 | // Indicates that a provider is compiled against a new enough | ||
334 | // version of core to support the GetSchema method. | ||
335 | SchemaAvailable: true, | ||
308 | }) | 336 | }) |
309 | } | 337 | } |
310 | 338 | ||
@@ -382,7 +410,7 @@ func (p *Provider) ReadDataDiff( | |||
382 | return nil, fmt.Errorf("unknown data source: %s", info.Type) | 410 | return nil, fmt.Errorf("unknown data source: %s", info.Type) |
383 | } | 411 | } |
384 | 412 | ||
385 | return r.Diff(nil, c) | 413 | return r.Diff(nil, c, p.meta) |
386 | } | 414 | } |
387 | 415 | ||
388 | // RefreshData implementation of terraform.ResourceProvider interface. | 416 | // RefreshData implementation of terraform.ResourceProvider interface. |
@@ -410,6 +438,10 @@ func (p *Provider) DataSources() []terraform.DataSource { | |||
410 | for _, k := range keys { | 438 | for _, k := range keys { |
411 | result = append(result, terraform.DataSource{ | 439 | result = append(result, terraform.DataSource{ |
412 | Name: k, | 440 | Name: k, |
441 | |||
442 | // Indicates that a provider is compiled against a new enough | ||
443 | // version of core to support the GetSchema method. | ||
444 | SchemaAvailable: true, | ||
413 | }) | 445 | }) |
414 | } | 446 | } |
415 | 447 | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go index 476192e..a8d42db 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/provisioner.go | |||
@@ -146,7 +146,7 @@ func (p *Provisioner) Apply( | |||
146 | } | 146 | } |
147 | 147 | ||
148 | sm := schemaMap(p.ConnSchema) | 148 | sm := schemaMap(p.ConnSchema) |
149 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c)) | 149 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil) |
150 | if err != nil { | 150 | if err != nil { |
151 | return err | 151 | return err |
152 | } | 152 | } |
@@ -160,7 +160,7 @@ func (p *Provisioner) Apply( | |||
160 | // Build the configuration data. Doing this requires making a "diff" | 160 | // Build the configuration data. Doing this requires making a "diff" |
161 | // even though that's never used. We use that just to get the correct types. | 161 | // even though that's never used. We use that just to get the correct types. |
162 | configMap := schemaMap(p.Schema) | 162 | configMap := schemaMap(p.Schema) |
163 | diff, err := configMap.Diff(nil, c) | 163 | diff, err := configMap.Diff(nil, c, nil, nil) |
164 | if err != nil { | 164 | if err != nil { |
165 | return err | 165 | return err |
166 | } | 166 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go index ddba109..d3be2d6 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go | |||
@@ -85,6 +85,37 @@ type Resource struct { | |||
85 | Delete DeleteFunc | 85 | Delete DeleteFunc |
86 | Exists ExistsFunc | 86 | Exists ExistsFunc |
87 | 87 | ||
88 | // CustomizeDiff is a custom function for working with the diff that | ||
89 | // Terraform has created for this resource - it can be used to customize the | ||
90 | // diff that has been created, diff values not controlled by configuration, | ||
91 | // or even veto the diff altogether and abort the plan. It is passed a | ||
92 | // *ResourceDiff, a structure similar to ResourceData but lacking most write | ||
93 | // functions like Set, while introducing new functions that work with the | ||
94 | // diff such as SetNew, SetNewComputed, and ForceNew. | ||
95 | // | ||
96 | // The phases Terraform runs this in, and the state available via functions | ||
97 | // like Get and GetChange, are as follows: | ||
98 | // | ||
99 | // * New resource: One run with no state | ||
100 | // * Existing resource: One run with state | ||
101 | // * Existing resource, forced new: One run with state (before ForceNew), | ||
102 | // then one run without state (as if new resource) | ||
103 | // * Tainted resource: No runs (custom diff logic is skipped) | ||
104 | // * Destroy: No runs (standard diff logic is skipped on destroy diffs) | ||
105 | // | ||
106 | // This function needs to be resilient to support all scenarios. | ||
107 | // | ||
108 | // If this function needs to access external API resources, remember to flag | ||
109 | // the RequiresRefresh attribute mentioned below to ensure that | ||
110 | // -refresh=false is blocked when running plan or apply, as this means that | ||
111 | // this resource requires refresh-like behaviour to work effectively. | ||
112 | // | ||
113 | // For the most part, only computed fields can be customized by this | ||
114 | // function. | ||
115 | // | ||
116 | // This function is only allowed on regular resources (not data sources). | ||
117 | CustomizeDiff CustomizeDiffFunc | ||
118 | |||
88 | // Importer is the ResourceImporter implementation for this resource. | 119 | // Importer is the ResourceImporter implementation for this resource. |
89 | // If this is nil, then this resource does not support importing. If | 120 | // If this is nil, then this resource does not support importing. If |
90 | // this is non-nil, then it supports importing and ResourceImporter | 121 | // this is non-nil, then it supports importing and ResourceImporter |
@@ -93,9 +124,7 @@ type Resource struct { | |||
93 | Importer *ResourceImporter | 124 | Importer *ResourceImporter |
94 | 125 | ||
95 | // If non-empty, this string is emitted as a warning during Validate. | 126 | // If non-empty, this string is emitted as a warning during Validate. |
96 | // This is a private interface for now, for use by DataSourceResourceShim, | 127 | DeprecationMessage string |
97 | // and not for general use. (But maybe later...) | ||
98 | deprecationMessage string | ||
99 | 128 | ||
100 | // Timeouts allow users to specify specific time durations in which an | 129 | // Timeouts allow users to specify specific time durations in which an |
101 | // operation should time out, to allow them to extend an action to suit their | 130 | // operation should time out, to allow them to extend an action to suit their |
@@ -126,6 +155,9 @@ type ExistsFunc func(*ResourceData, interface{}) (bool, error) | |||
126 | type StateMigrateFunc func( | 155 | type StateMigrateFunc func( |
127 | int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) | 156 | int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) |
128 | 157 | ||
158 | // See Resource documentation. | ||
159 | type CustomizeDiffFunc func(*ResourceDiff, interface{}) error | ||
160 | |||
129 | // Apply creates, updates, and/or deletes a resource. | 161 | // Apply creates, updates, and/or deletes a resource. |
130 | func (r *Resource) Apply( | 162 | func (r *Resource) Apply( |
131 | s *terraform.InstanceState, | 163 | s *terraform.InstanceState, |
@@ -202,11 +234,11 @@ func (r *Resource) Apply( | |||
202 | return r.recordCurrentSchemaVersion(data.State()), err | 234 | return r.recordCurrentSchemaVersion(data.State()), err |
203 | } | 235 | } |
204 | 236 | ||
205 | // Diff returns a diff of this resource and is API compatible with the | 237 | // Diff returns a diff of this resource. |
206 | // ResourceProvider interface. | ||
207 | func (r *Resource) Diff( | 238 | func (r *Resource) Diff( |
208 | s *terraform.InstanceState, | 239 | s *terraform.InstanceState, |
209 | c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { | 240 | c *terraform.ResourceConfig, |
241 | meta interface{}) (*terraform.InstanceDiff, error) { | ||
210 | 242 | ||
211 | t := &ResourceTimeout{} | 243 | t := &ResourceTimeout{} |
212 | err := t.ConfigDecode(r, c) | 244 | err := t.ConfigDecode(r, c) |
@@ -215,7 +247,7 @@ func (r *Resource) Diff( | |||
215 | return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) | 247 | return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) |
216 | } | 248 | } |
217 | 249 | ||
218 | instanceDiff, err := schemaMap(r.Schema).Diff(s, c) | 250 | instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta) |
219 | if err != nil { | 251 | if err != nil { |
220 | return instanceDiff, err | 252 | return instanceDiff, err |
221 | } | 253 | } |
@@ -235,8 +267,8 @@ func (r *Resource) Diff( | |||
235 | func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { | 267 | func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { |
236 | warns, errs := schemaMap(r.Schema).Validate(c) | 268 | warns, errs := schemaMap(r.Schema).Validate(c) |
237 | 269 | ||
238 | if r.deprecationMessage != "" { | 270 | if r.DeprecationMessage != "" { |
239 | warns = append(warns, r.deprecationMessage) | 271 | warns = append(warns, r.DeprecationMessage) |
240 | } | 272 | } |
241 | 273 | ||
242 | return warns, errs | 274 | return warns, errs |
@@ -248,7 +280,6 @@ func (r *Resource) ReadDataApply( | |||
248 | d *terraform.InstanceDiff, | 280 | d *terraform.InstanceDiff, |
249 | meta interface{}, | 281 | meta interface{}, |
250 | ) (*terraform.InstanceState, error) { | 282 | ) (*terraform.InstanceState, error) { |
251 | |||
252 | // Data sources are always built completely from scratch | 283 | // Data sources are always built completely from scratch |
253 | // on each read, so the source state is always nil. | 284 | // on each read, so the source state is always nil. |
254 | data, err := schemaMap(r.Schema).Data(nil, d) | 285 | data, err := schemaMap(r.Schema).Data(nil, d) |
@@ -346,6 +377,11 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error | |||
346 | if r.Create != nil || r.Update != nil || r.Delete != nil { | 377 | if r.Create != nil || r.Update != nil || r.Delete != nil { |
347 | return fmt.Errorf("must not implement Create, Update or Delete") | 378 | return fmt.Errorf("must not implement Create, Update or Delete") |
348 | } | 379 | } |
380 | |||
381 | // CustomizeDiff cannot be defined for read-only resources | ||
382 | if r.CustomizeDiff != nil { | ||
383 | return fmt.Errorf("cannot implement CustomizeDiff") | ||
384 | } | ||
349 | } | 385 | } |
350 | 386 | ||
351 | tsm := topSchemaMap | 387 | tsm := topSchemaMap |
@@ -393,19 +429,43 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error | |||
393 | return err | 429 | return err |
394 | } | 430 | } |
395 | } | 431 | } |
432 | |||
433 | for k, f := range tsm { | ||
434 | if isReservedResourceFieldName(k, f) { | ||
435 | return fmt.Errorf("%s is a reserved field name", k) | ||
436 | } | ||
437 | } | ||
396 | } | 438 | } |
397 | 439 | ||
398 | // Resource-specific checks | 440 | // Data source |
399 | for k, _ := range tsm { | 441 | if r.isTopLevel() && !writable { |
400 | if isReservedResourceFieldName(k) { | 442 | tsm = schemaMap(r.Schema) |
401 | return fmt.Errorf("%s is a reserved field name for a resource", k) | 443 | for k, _ := range tsm { |
444 | if isReservedDataSourceFieldName(k) { | ||
445 | return fmt.Errorf("%s is a reserved field name", k) | ||
446 | } | ||
402 | } | 447 | } |
403 | } | 448 | } |
404 | 449 | ||
405 | return schemaMap(r.Schema).InternalValidate(tsm) | 450 | return schemaMap(r.Schema).InternalValidate(tsm) |
406 | } | 451 | } |
407 | 452 | ||
408 | func isReservedResourceFieldName(name string) bool { | 453 | func isReservedDataSourceFieldName(name string) bool { |
454 | for _, reservedName := range config.ReservedDataSourceFields { | ||
455 | if name == reservedName { | ||
456 | return true | ||
457 | } | ||
458 | } | ||
459 | return false | ||
460 | } | ||
461 | |||
462 | func isReservedResourceFieldName(name string, s *Schema) bool { | ||
463 | // Allow phasing out "id" | ||
464 | // See https://github.com/terraform-providers/terraform-provider-aws/pull/1626#issuecomment-328881415 | ||
465 | if name == "id" && (s.Deprecated != "" || s.Removed != "") { | ||
466 | return false | ||
467 | } | ||
468 | |||
409 | for _, reservedName := range config.ReservedResourceFields { | 469 | for _, reservedName := range config.ReservedResourceFields { |
410 | if name == reservedName { | 470 | if name == reservedName { |
411 | return true | 471 | return true |
@@ -430,6 +490,12 @@ func (r *Resource) Data(s *terraform.InstanceState) *ResourceData { | |||
430 | panic(err) | 490 | panic(err) |
431 | } | 491 | } |
432 | 492 | ||
493 | // load the Resource timeouts | ||
494 | result.timeouts = r.Timeouts | ||
495 | if result.timeouts == nil { | ||
496 | result.timeouts = &ResourceTimeout{} | ||
497 | } | ||
498 | |||
433 | // Set the schema version to latest by default | 499 | // Set the schema version to latest by default |
434 | result.meta = map[string]interface{}{ | 500 | result.meta = map[string]interface{}{ |
435 | "schema_version": strconv.Itoa(r.SchemaVersion), | 501 | "schema_version": strconv.Itoa(r.SchemaVersion), |
@@ -450,7 +516,7 @@ func (r *Resource) TestResourceData() *ResourceData { | |||
450 | // Returns true if the resource is "top level" i.e. not a sub-resource. | 516 | // Returns true if the resource is "top level" i.e. not a sub-resource. |
451 | func (r *Resource) isTopLevel() bool { | 517 | func (r *Resource) isTopLevel() bool { |
452 | // TODO: This is a heuristic; replace with a definitive attribute? | 518 | // TODO: This is a heuristic; replace with a definitive attribute? |
453 | return r.Create != nil | 519 | return (r.Create != nil || r.Read != nil) |
454 | } | 520 | } |
455 | 521 | ||
456 | // Determines if a given InstanceState needs to be migrated by checking the | 522 | // Determines if a given InstanceState needs to be migrated by checking the |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go index b2bc8f6..6cc01ee 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_data.go | |||
@@ -35,6 +35,8 @@ type ResourceData struct { | |||
35 | partialMap map[string]struct{} | 35 | partialMap map[string]struct{} |
36 | once sync.Once | 36 | once sync.Once |
37 | isNew bool | 37 | isNew bool |
38 | |||
39 | panicOnError bool | ||
38 | } | 40 | } |
39 | 41 | ||
40 | // getResult is the internal structure that is generated when a Get | 42 | // getResult is the internal structure that is generated when a Get |
@@ -104,6 +106,22 @@ func (d *ResourceData) GetOk(key string) (interface{}, bool) { | |||
104 | return r.Value, exists | 106 | return r.Value, exists |
105 | } | 107 | } |
106 | 108 | ||
109 | // GetOkExists returns the data for a given key and whether or not the key | ||
110 | // has been set to a non-zero value. This is only useful for determining | ||
111 | // if boolean attributes have been set, if they are Optional but do not | ||
112 | // have a Default value. | ||
113 | // | ||
114 | // This is nearly the same function as GetOk, yet it does not check | ||
115 | // for the zero value of the attribute's type. This allows for attributes | ||
116 | // without a default, to fully check for a literal assignment, regardless | ||
117 | // of the zero-value for that type. | ||
118 | // This should only be used if absolutely required/needed. | ||
119 | func (d *ResourceData) GetOkExists(key string) (interface{}, bool) { | ||
120 | r := d.getRaw(key, getSourceSet) | ||
121 | exists := r.Exists && !r.Computed | ||
122 | return r.Value, exists | ||
123 | } | ||
124 | |||
107 | func (d *ResourceData) getRaw(key string, level getSource) getResult { | 125 | func (d *ResourceData) getRaw(key string, level getSource) getResult { |
108 | var parts []string | 126 | var parts []string |
109 | if key != "" { | 127 | if key != "" { |
@@ -168,7 +186,11 @@ func (d *ResourceData) Set(key string, value interface{}) error { | |||
168 | } | 186 | } |
169 | } | 187 | } |
170 | 188 | ||
171 | return d.setWriter.WriteField(strings.Split(key, "."), value) | 189 | err := d.setWriter.WriteField(strings.Split(key, "."), value) |
190 | if err != nil && d.panicOnError { | ||
191 | panic(err) | ||
192 | } | ||
193 | return err | ||
172 | } | 194 | } |
173 | 195 | ||
174 | // SetPartial adds the key to the final state output while | 196 | // SetPartial adds the key to the final state output while |
@@ -293,6 +315,7 @@ func (d *ResourceData) State() *terraform.InstanceState { | |||
293 | 315 | ||
294 | mapW := &MapFieldWriter{Schema: d.schema} | 316 | mapW := &MapFieldWriter{Schema: d.schema} |
295 | if err := mapW.WriteField(nil, rawMap); err != nil { | 317 | if err := mapW.WriteField(nil, rawMap); err != nil { |
318 | log.Printf("[ERR] Error writing fields: %s", err) | ||
296 | return nil | 319 | return nil |
297 | } | 320 | } |
298 | 321 | ||
@@ -344,6 +367,13 @@ func (d *ResourceData) State() *terraform.InstanceState { | |||
344 | func (d *ResourceData) Timeout(key string) time.Duration { | 367 | func (d *ResourceData) Timeout(key string) time.Duration { |
345 | key = strings.ToLower(key) | 368 | key = strings.ToLower(key) |
346 | 369 | ||
370 | // System default of 20 minutes | ||
371 | defaultTimeout := 20 * time.Minute | ||
372 | |||
373 | if d.timeouts == nil { | ||
374 | return defaultTimeout | ||
375 | } | ||
376 | |||
347 | var timeout *time.Duration | 377 | var timeout *time.Duration |
348 | switch key { | 378 | switch key { |
349 | case TimeoutCreate: | 379 | case TimeoutCreate: |
@@ -364,8 +394,7 @@ func (d *ResourceData) Timeout(key string) time.Duration { | |||
364 | return *d.timeouts.Default | 394 | return *d.timeouts.Default |
365 | } | 395 | } |
366 | 396 | ||
367 | // Return system default of 20 minutes | 397 | return defaultTimeout |
368 | return 20 * time.Minute | ||
369 | } | 398 | } |
370 | 399 | ||
371 | func (d *ResourceData) init() { | 400 | func (d *ResourceData) init() { |
@@ -423,7 +452,7 @@ func (d *ResourceData) init() { | |||
423 | } | 452 | } |
424 | 453 | ||
425 | func (d *ResourceData) diffChange( | 454 | func (d *ResourceData) diffChange( |
426 | k string) (interface{}, interface{}, bool, bool) { | 455 | k string) (interface{}, interface{}, bool, bool, bool) { |
427 | // Get the change between the state and the config. | 456 | // Get the change between the state and the config. |
428 | o, n := d.getChange(k, getSourceState, getSourceConfig|getSourceExact) | 457 | o, n := d.getChange(k, getSourceState, getSourceConfig|getSourceExact) |
429 | if !o.Exists { | 458 | if !o.Exists { |
@@ -434,7 +463,7 @@ func (d *ResourceData) diffChange( | |||
434 | } | 463 | } |
435 | 464 | ||
436 | // Return the old, new, and whether there is a change | 465 | // Return the old, new, and whether there is a change |
437 | return o.Value, n.Value, !reflect.DeepEqual(o.Value, n.Value), n.Computed | 466 | return o.Value, n.Value, !reflect.DeepEqual(o.Value, n.Value), n.Computed, false |
438 | } | 467 | } |
439 | 468 | ||
440 | func (d *ResourceData) getChange( | 469 | func (d *ResourceData) getChange( |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go new file mode 100644 index 0000000..7db3dec --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_diff.go | |||
@@ -0,0 +1,559 @@ | |||
1 | package schema | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | "reflect" | ||
7 | "strings" | ||
8 | "sync" | ||
9 | |||
10 | "github.com/hashicorp/terraform/terraform" | ||
11 | ) | ||
12 | |||
13 | // newValueWriter is a minor re-implementation of MapFieldWriter to include | ||
14 | // keys that should be marked as computed, to represent the new part of a | ||
15 | // pseudo-diff. | ||
16 | type newValueWriter struct { | ||
17 | *MapFieldWriter | ||
18 | |||
19 | // A list of keys that should be marked as computed. | ||
20 | computedKeys map[string]bool | ||
21 | |||
22 | // A lock to prevent races on writes. The underlying writer will have one as | ||
23 | // well - this is for computed keys. | ||
24 | lock sync.Mutex | ||
25 | |||
26 | // To be used with init. | ||
27 | once sync.Once | ||
28 | } | ||
29 | |||
30 | // init performs any initialization tasks for the newValueWriter. | ||
31 | func (w *newValueWriter) init() { | ||
32 | if w.computedKeys == nil { | ||
33 | w.computedKeys = make(map[string]bool) | ||
34 | } | ||
35 | } | ||
36 | |||
37 | // WriteField overrides MapValueWriter's WriteField, adding the ability to flag | ||
38 | // the address as computed. | ||
39 | func (w *newValueWriter) WriteField(address []string, value interface{}, computed bool) error { | ||
40 | // Fail the write if we have a non-nil value and computed is true. | ||
41 | // NewComputed values should not have a value when written. | ||
42 | if value != nil && computed { | ||
43 | return errors.New("Non-nil value with computed set") | ||
44 | } | ||
45 | |||
46 | if err := w.MapFieldWriter.WriteField(address, value); err != nil { | ||
47 | return err | ||
48 | } | ||
49 | |||
50 | w.once.Do(w.init) | ||
51 | |||
52 | w.lock.Lock() | ||
53 | defer w.lock.Unlock() | ||
54 | if computed { | ||
55 | w.computedKeys[strings.Join(address, ".")] = true | ||
56 | } | ||
57 | return nil | ||
58 | } | ||
59 | |||
60 | // ComputedKeysMap returns the underlying computed keys map. | ||
61 | func (w *newValueWriter) ComputedKeysMap() map[string]bool { | ||
62 | w.once.Do(w.init) | ||
63 | return w.computedKeys | ||
64 | } | ||
65 | |||
66 | // newValueReader is a minor re-implementation of MapFieldReader and is the | ||
67 | // read counterpart to MapValueWriter, allowing the read of keys flagged as | ||
68 | // computed to accommodate the diff override logic in ResourceDiff. | ||
69 | type newValueReader struct { | ||
70 | *MapFieldReader | ||
71 | |||
72 | // The list of computed keys from a newValueWriter. | ||
73 | computedKeys map[string]bool | ||
74 | } | ||
75 | |||
76 | // ReadField reads the values from the underlying writer, returning the | ||
77 | // computed value if it is found as well. | ||
78 | func (r *newValueReader) ReadField(address []string) (FieldReadResult, error) { | ||
79 | addrKey := strings.Join(address, ".") | ||
80 | v, err := r.MapFieldReader.ReadField(address) | ||
81 | if err != nil { | ||
82 | return FieldReadResult{}, err | ||
83 | } | ||
84 | for computedKey := range r.computedKeys { | ||
85 | if childAddrOf(addrKey, computedKey) { | ||
86 | if strings.HasSuffix(addrKey, ".#") { | ||
87 | // This is a count value for a list or set that has been marked as | ||
88 | // computed, or a sub-list/sub-set of a complex resource that has | ||
89 | // been marked as computed. We need to pass through to other readers | ||
90 | // so that an accurate previous count can be fetched for the diff. | ||
91 | v.Exists = false | ||
92 | } | ||
93 | v.Computed = true | ||
94 | } | ||
95 | } | ||
96 | |||
97 | return v, nil | ||
98 | } | ||
99 | |||
100 | // ResourceDiff is used to query and make custom changes to an in-flight diff. | ||
101 | // It can be used to veto particular changes in the diff, customize the diff | ||
102 | // that has been created, or diff values not controlled by config. | ||
103 | // | ||
104 | // The object functions similar to ResourceData, however most notably lacks | ||
105 | // Set, SetPartial, and Partial, as it should be used to change diff values | ||
106 | // only. Most other first-class ResourceData functions exist, namely Get, | ||
107 | // GetOk, HasChange, and GetChange exist. | ||
108 | // | ||
109 | // All functions in ResourceDiff, save for ForceNew, can only be used on | ||
110 | // computed fields. | ||
111 | type ResourceDiff struct { | ||
112 | // The schema for the resource being worked on. | ||
113 | schema map[string]*Schema | ||
114 | |||
115 | // The current config for this resource. | ||
116 | config *terraform.ResourceConfig | ||
117 | |||
118 | // The state for this resource as it exists post-refresh, after the initial | ||
119 | // diff. | ||
120 | state *terraform.InstanceState | ||
121 | |||
122 | // The diff created by Terraform. This diff is used, along with state, | ||
123 | // config, and custom-set diff data, to provide a multi-level reader | ||
124 | // experience similar to ResourceData. | ||
125 | diff *terraform.InstanceDiff | ||
126 | |||
127 | // The internal reader structure that contains the state, config, the default | ||
128 | // diff, and the new diff. | ||
129 | multiReader *MultiLevelFieldReader | ||
130 | |||
131 | // A writer that writes overridden new fields. | ||
132 | newWriter *newValueWriter | ||
133 | |||
134 | // Tracks which keys have been updated by ResourceDiff to ensure that the | ||
135 | // diff does not get re-run on keys that were not touched, or diffs that were | ||
136 | // just removed (re-running on the latter would just roll back the removal). | ||
137 | updatedKeys map[string]bool | ||
138 | |||
139 | // Tracks which keys were flagged as forceNew. These keys are not saved in | ||
140 | // newWriter, but we need to track them so that they can be re-diffed later. | ||
141 | forcedNewKeys map[string]bool | ||
142 | } | ||
143 | |||
144 | // newResourceDiff creates a new ResourceDiff instance. | ||
145 | func newResourceDiff(schema map[string]*Schema, config *terraform.ResourceConfig, state *terraform.InstanceState, diff *terraform.InstanceDiff) *ResourceDiff { | ||
146 | d := &ResourceDiff{ | ||
147 | config: config, | ||
148 | state: state, | ||
149 | diff: diff, | ||
150 | schema: schema, | ||
151 | } | ||
152 | |||
153 | d.newWriter = &newValueWriter{ | ||
154 | MapFieldWriter: &MapFieldWriter{Schema: d.schema}, | ||
155 | } | ||
156 | readers := make(map[string]FieldReader) | ||
157 | var stateAttributes map[string]string | ||
158 | if d.state != nil { | ||
159 | stateAttributes = d.state.Attributes | ||
160 | readers["state"] = &MapFieldReader{ | ||
161 | Schema: d.schema, | ||
162 | Map: BasicMapReader(stateAttributes), | ||
163 | } | ||
164 | } | ||
165 | if d.config != nil { | ||
166 | readers["config"] = &ConfigFieldReader{ | ||
167 | Schema: d.schema, | ||
168 | Config: d.config, | ||
169 | } | ||
170 | } | ||
171 | if d.diff != nil { | ||
172 | readers["diff"] = &DiffFieldReader{ | ||
173 | Schema: d.schema, | ||
174 | Diff: d.diff, | ||
175 | Source: &MultiLevelFieldReader{ | ||
176 | Levels: []string{"state", "config"}, | ||
177 | Readers: readers, | ||
178 | }, | ||
179 | } | ||
180 | } | ||
181 | readers["newDiff"] = &newValueReader{ | ||
182 | MapFieldReader: &MapFieldReader{ | ||
183 | Schema: d.schema, | ||
184 | Map: BasicMapReader(d.newWriter.Map()), | ||
185 | }, | ||
186 | computedKeys: d.newWriter.ComputedKeysMap(), | ||
187 | } | ||
188 | d.multiReader = &MultiLevelFieldReader{ | ||
189 | Levels: []string{ | ||
190 | "state", | ||
191 | "config", | ||
192 | "diff", | ||
193 | "newDiff", | ||
194 | }, | ||
195 | |||
196 | Readers: readers, | ||
197 | } | ||
198 | |||
199 | d.updatedKeys = make(map[string]bool) | ||
200 | d.forcedNewKeys = make(map[string]bool) | ||
201 | |||
202 | return d | ||
203 | } | ||
204 | |||
205 | // UpdatedKeys returns the keys that were updated by this ResourceDiff run. | ||
206 | // These are the only keys that a diff should be re-calculated for. | ||
207 | // | ||
208 | // This is the combined result of both keys for which diff values were updated | ||
209 | // for or cleared, and also keys that were flagged to be re-diffed as a result | ||
210 | // of ForceNew. | ||
211 | func (d *ResourceDiff) UpdatedKeys() []string { | ||
212 | var s []string | ||
213 | for k := range d.updatedKeys { | ||
214 | s = append(s, k) | ||
215 | } | ||
216 | for k := range d.forcedNewKeys { | ||
217 | for _, l := range s { | ||
218 | if k == l { | ||
219 | break | ||
220 | } | ||
221 | } | ||
222 | s = append(s, k) | ||
223 | } | ||
224 | return s | ||
225 | } | ||
226 | |||
227 | // Clear wipes the diff for a particular key. It is called by ResourceDiff's | ||
228 | // functionality to remove any possibility of conflicts, but can be called on | ||
229 | // its own to just remove a specific key from the diff completely. | ||
230 | // | ||
231 | // Note that this does not wipe an override. This function is only allowed on | ||
232 | // computed keys. | ||
233 | func (d *ResourceDiff) Clear(key string) error { | ||
234 | if err := d.checkKey(key, "Clear", true); err != nil { | ||
235 | return err | ||
236 | } | ||
237 | |||
238 | return d.clear(key) | ||
239 | } | ||
240 | |||
241 | func (d *ResourceDiff) clear(key string) error { | ||
242 | // Check the schema to make sure that this key exists first. | ||
243 | schemaL := addrToSchema(strings.Split(key, "."), d.schema) | ||
244 | if len(schemaL) == 0 { | ||
245 | return fmt.Errorf("%s is not a valid key", key) | ||
246 | } | ||
247 | |||
248 | for k := range d.diff.Attributes { | ||
249 | if strings.HasPrefix(k, key) { | ||
250 | delete(d.diff.Attributes, k) | ||
251 | } | ||
252 | } | ||
253 | return nil | ||
254 | } | ||
255 | |||
256 | // GetChangedKeysPrefix helps to implement Resource.CustomizeDiff | ||
257 | // where we need to act on all nested fields | ||
258 | // without calling out each one separately | ||
259 | func (d *ResourceDiff) GetChangedKeysPrefix(prefix string) []string { | ||
260 | keys := make([]string, 0) | ||
261 | for k := range d.diff.Attributes { | ||
262 | if strings.HasPrefix(k, prefix) { | ||
263 | keys = append(keys, k) | ||
264 | } | ||
265 | } | ||
266 | return keys | ||
267 | } | ||
268 | |||
269 | // diffChange helps to implement resourceDiffer and derives its change values | ||
270 | // from ResourceDiff's own change data, in addition to existing diff, config, and state. | ||
271 | func (d *ResourceDiff) diffChange(key string) (interface{}, interface{}, bool, bool, bool) { | ||
272 | old, new, customized := d.getChange(key) | ||
273 | |||
274 | if !old.Exists { | ||
275 | old.Value = nil | ||
276 | } | ||
277 | if !new.Exists || d.removed(key) { | ||
278 | new.Value = nil | ||
279 | } | ||
280 | |||
281 | return old.Value, new.Value, !reflect.DeepEqual(old.Value, new.Value), new.Computed, customized | ||
282 | } | ||
283 | |||
284 | // SetNew is used to set a new diff value for the mentioned key. The value must | ||
285 | // be correct for the attribute's schema (mostly relevant for maps, lists, and | ||
286 | // sets). The original value from the state is used as the old value. | ||
287 | // | ||
288 | // This function is only allowed on computed attributes. | ||
289 | func (d *ResourceDiff) SetNew(key string, value interface{}) error { | ||
290 | if err := d.checkKey(key, "SetNew", false); err != nil { | ||
291 | return err | ||
292 | } | ||
293 | |||
294 | return d.setDiff(key, value, false) | ||
295 | } | ||
296 | |||
297 | // SetNewComputed functions like SetNew, except that it blanks out a new value | ||
298 | // and marks it as computed. | ||
299 | // | ||
300 | // This function is only allowed on computed attributes. | ||
301 | func (d *ResourceDiff) SetNewComputed(key string) error { | ||
302 | if err := d.checkKey(key, "SetNewComputed", false); err != nil { | ||
303 | return err | ||
304 | } | ||
305 | |||
306 | return d.setDiff(key, nil, true) | ||
307 | } | ||
308 | |||
309 | // setDiff performs common diff setting behaviour. | ||
310 | func (d *ResourceDiff) setDiff(key string, new interface{}, computed bool) error { | ||
311 | if err := d.clear(key); err != nil { | ||
312 | return err | ||
313 | } | ||
314 | |||
315 | if err := d.newWriter.WriteField(strings.Split(key, "."), new, computed); err != nil { | ||
316 | return fmt.Errorf("Cannot set new diff value for key %s: %s", key, err) | ||
317 | } | ||
318 | |||
319 | d.updatedKeys[key] = true | ||
320 | |||
321 | return nil | ||
322 | } | ||
323 | |||
324 | // ForceNew force-flags ForceNew in the schema for a specific key, and | ||
325 | // re-calculates its diff, effectively causing this attribute to force a new | ||
326 | // resource. | ||
327 | // | ||
328 | // Keep in mind that forcing a new resource will force a second run of the | ||
329 | // resource's CustomizeDiff function (with a new ResourceDiff) once the current | ||
330 | // one has completed. This second run is performed without state. This behavior | ||
331 | // will be the same as if a new resource is being created and is performed to | ||
332 | // ensure that the diff looks like the diff for a new resource as much as | ||
333 | // possible. CustomizeDiff should expect such a scenario and act correctly. | ||
334 | // | ||
335 | // This function is a no-op/error if there is no diff. | ||
336 | // | ||
337 | // Note that the change to schema is permanent for the lifecycle of this | ||
338 | // specific ResourceDiff instance. | ||
339 | func (d *ResourceDiff) ForceNew(key string) error { | ||
340 | if !d.HasChange(key) { | ||
341 | return fmt.Errorf("ForceNew: No changes for %s", key) | ||
342 | } | ||
343 | |||
344 | keyParts := strings.Split(key, ".") | ||
345 | var schema *Schema | ||
346 | schemaL := addrToSchema(keyParts, d.schema) | ||
347 | if len(schemaL) > 0 { | ||
348 | schema = schemaL[len(schemaL)-1] | ||
349 | } else { | ||
350 | return fmt.Errorf("ForceNew: %s is not a valid key", key) | ||
351 | } | ||
352 | |||
353 | schema.ForceNew = true | ||
354 | |||
355 | // Flag this for a re-diff. Don't save any values to guarantee that existing | ||
356 | // diffs aren't messed with, as this gets messy when dealing with complex | ||
357 | // structures, zero values, etc. | ||
358 | d.forcedNewKeys[keyParts[0]] = true | ||
359 | |||
360 | return nil | ||
361 | } | ||
362 | |||
363 | // Get hands off to ResourceData.Get. | ||
364 | func (d *ResourceDiff) Get(key string) interface{} { | ||
365 | r, _ := d.GetOk(key) | ||
366 | return r | ||
367 | } | ||
368 | |||
369 | // GetChange gets the change between the state and diff, checking first to see | ||
370 | // if a overridden diff exists. | ||
371 | // | ||
372 | // This implementation differs from ResourceData's in the way that we first get | ||
373 | // results from the exact levels for the new diff, then from state and diff as | ||
374 | // per normal. | ||
375 | func (d *ResourceDiff) GetChange(key string) (interface{}, interface{}) { | ||
376 | old, new, _ := d.getChange(key) | ||
377 | return old.Value, new.Value | ||
378 | } | ||
379 | |||
380 | // GetOk functions the same way as ResourceData.GetOk, but it also checks the | ||
381 | // new diff levels to provide data consistent with the current state of the | ||
382 | // customized diff. | ||
383 | func (d *ResourceDiff) GetOk(key string) (interface{}, bool) { | ||
384 | r := d.get(strings.Split(key, "."), "newDiff") | ||
385 | exists := r.Exists && !r.Computed | ||
386 | if exists { | ||
387 | // If it exists, we also want to verify it is not the zero-value. | ||
388 | value := r.Value | ||
389 | zero := r.Schema.Type.Zero() | ||
390 | |||
391 | if eq, ok := value.(Equal); ok { | ||
392 | exists = !eq.Equal(zero) | ||
393 | } else { | ||
394 | exists = !reflect.DeepEqual(value, zero) | ||
395 | } | ||
396 | } | ||
397 | |||
398 | return r.Value, exists | ||
399 | } | ||
400 | |||
401 | // GetOkExists functions the same way as GetOkExists within ResourceData, but | ||
402 | // it also checks the new diff levels to provide data consistent with the | ||
403 | // current state of the customized diff. | ||
404 | // | ||
405 | // This is nearly the same function as GetOk, yet it does not check | ||
406 | // for the zero value of the attribute's type. This allows for attributes | ||
407 | // without a default, to fully check for a literal assignment, regardless | ||
408 | // of the zero-value for that type. | ||
409 | func (d *ResourceDiff) GetOkExists(key string) (interface{}, bool) { | ||
410 | r := d.get(strings.Split(key, "."), "newDiff") | ||
411 | exists := r.Exists && !r.Computed | ||
412 | return r.Value, exists | ||
413 | } | ||
414 | |||
415 | // NewValueKnown returns true if the new value for the given key is available | ||
416 | // as its final value at diff time. If the return value is false, this means | ||
417 | // either the value is based of interpolation that was unavailable at diff | ||
418 | // time, or that the value was explicitly marked as computed by SetNewComputed. | ||
419 | func (d *ResourceDiff) NewValueKnown(key string) bool { | ||
420 | r := d.get(strings.Split(key, "."), "newDiff") | ||
421 | return !r.Computed | ||
422 | } | ||
423 | |||
424 | // HasChange checks to see if there is a change between state and the diff, or | ||
425 | // in the overridden diff. | ||
426 | func (d *ResourceDiff) HasChange(key string) bool { | ||
427 | old, new := d.GetChange(key) | ||
428 | |||
429 | // If the type implements the Equal interface, then call that | ||
430 | // instead of just doing a reflect.DeepEqual. An example where this is | ||
431 | // needed is *Set | ||
432 | if eq, ok := old.(Equal); ok { | ||
433 | return !eq.Equal(new) | ||
434 | } | ||
435 | |||
436 | return !reflect.DeepEqual(old, new) | ||
437 | } | ||
438 | |||
439 | // Id returns the ID of this resource. | ||
440 | // | ||
441 | // Note that technically, ID does not change during diffs (it either has | ||
442 | // already changed in the refresh, or will change on update), hence we do not | ||
443 | // support updating the ID or fetching it from anything else other than state. | ||
444 | func (d *ResourceDiff) Id() string { | ||
445 | var result string | ||
446 | |||
447 | if d.state != nil { | ||
448 | result = d.state.ID | ||
449 | } | ||
450 | return result | ||
451 | } | ||
452 | |||
453 | // getChange gets values from two different levels, designed for use in | ||
454 | // diffChange, HasChange, and GetChange. | ||
455 | // | ||
456 | // This implementation differs from ResourceData's in the way that we first get | ||
457 | // results from the exact levels for the new diff, then from state and diff as | ||
458 | // per normal. | ||
459 | func (d *ResourceDiff) getChange(key string) (getResult, getResult, bool) { | ||
460 | old := d.get(strings.Split(key, "."), "state") | ||
461 | var new getResult | ||
462 | for p := range d.updatedKeys { | ||
463 | if childAddrOf(key, p) { | ||
464 | new = d.getExact(strings.Split(key, "."), "newDiff") | ||
465 | return old, new, true | ||
466 | } | ||
467 | } | ||
468 | new = d.get(strings.Split(key, "."), "newDiff") | ||
469 | return old, new, false | ||
470 | } | ||
471 | |||
472 | // removed checks to see if the key is present in the existing, pre-customized | ||
473 | // diff and if it was marked as NewRemoved. | ||
474 | func (d *ResourceDiff) removed(k string) bool { | ||
475 | diff, ok := d.diff.Attributes[k] | ||
476 | if !ok { | ||
477 | return false | ||
478 | } | ||
479 | return diff.NewRemoved | ||
480 | } | ||
481 | |||
482 | // get performs the appropriate multi-level reader logic for ResourceDiff, | ||
483 | // starting at source. Refer to newResourceDiff for the level order. | ||
484 | func (d *ResourceDiff) get(addr []string, source string) getResult { | ||
485 | result, err := d.multiReader.ReadFieldMerge(addr, source) | ||
486 | if err != nil { | ||
487 | panic(err) | ||
488 | } | ||
489 | |||
490 | return d.finalizeResult(addr, result) | ||
491 | } | ||
492 | |||
493 | // getExact gets an attribute from the exact level referenced by source. | ||
494 | func (d *ResourceDiff) getExact(addr []string, source string) getResult { | ||
495 | result, err := d.multiReader.ReadFieldExact(addr, source) | ||
496 | if err != nil { | ||
497 | panic(err) | ||
498 | } | ||
499 | |||
500 | return d.finalizeResult(addr, result) | ||
501 | } | ||
502 | |||
503 | // finalizeResult does some post-processing of the result produced by get and getExact. | ||
504 | func (d *ResourceDiff) finalizeResult(addr []string, result FieldReadResult) getResult { | ||
505 | // If the result doesn't exist, then we set the value to the zero value | ||
506 | var schema *Schema | ||
507 | if schemaL := addrToSchema(addr, d.schema); len(schemaL) > 0 { | ||
508 | schema = schemaL[len(schemaL)-1] | ||
509 | } | ||
510 | |||
511 | if result.Value == nil && schema != nil { | ||
512 | result.Value = result.ValueOrZero(schema) | ||
513 | } | ||
514 | |||
515 | // Transform the FieldReadResult into a getResult. It might be worth | ||
516 | // merging these two structures one day. | ||
517 | return getResult{ | ||
518 | Value: result.Value, | ||
519 | ValueProcessed: result.ValueProcessed, | ||
520 | Computed: result.Computed, | ||
521 | Exists: result.Exists, | ||
522 | Schema: schema, | ||
523 | } | ||
524 | } | ||
525 | |||
526 | // childAddrOf does a comparison of two addresses to see if one is the child of | ||
527 | // the other. | ||
528 | func childAddrOf(child, parent string) bool { | ||
529 | cs := strings.Split(child, ".") | ||
530 | ps := strings.Split(parent, ".") | ||
531 | if len(ps) > len(cs) { | ||
532 | return false | ||
533 | } | ||
534 | return reflect.DeepEqual(ps, cs[:len(ps)]) | ||
535 | } | ||
536 | |||
537 | // checkKey checks the key to make sure it exists and is computed. | ||
538 | func (d *ResourceDiff) checkKey(key, caller string, nested bool) error { | ||
539 | var schema *Schema | ||
540 | if nested { | ||
541 | keyParts := strings.Split(key, ".") | ||
542 | schemaL := addrToSchema(keyParts, d.schema) | ||
543 | if len(schemaL) > 0 { | ||
544 | schema = schemaL[len(schemaL)-1] | ||
545 | } | ||
546 | } else { | ||
547 | s, ok := d.schema[key] | ||
548 | if ok { | ||
549 | schema = s | ||
550 | } | ||
551 | } | ||
552 | if schema == nil { | ||
553 | return fmt.Errorf("%s: invalid key: %s", caller, key) | ||
554 | } | ||
555 | if !schema.Computed { | ||
556 | return fmt.Errorf("%s only operates on computed keys - %s is not one", caller, key) | ||
557 | } | ||
558 | return nil | ||
559 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go index acb5618..0ea5aad 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go | |||
@@ -21,9 +21,13 @@ import ( | |||
21 | "strings" | 21 | "strings" |
22 | 22 | ||
23 | "github.com/hashicorp/terraform/terraform" | 23 | "github.com/hashicorp/terraform/terraform" |
24 | "github.com/mitchellh/copystructure" | ||
24 | "github.com/mitchellh/mapstructure" | 25 | "github.com/mitchellh/mapstructure" |
25 | ) | 26 | ) |
26 | 27 | ||
28 | // Name of ENV variable which (if not empty) prefers panic over error | ||
29 | const PanicOnErr = "TF_SCHEMA_PANIC_ON_ERROR" | ||
30 | |||
27 | // type used for schema package context keys | 31 | // type used for schema package context keys |
28 | type contextKey string | 32 | type contextKey string |
29 | 33 | ||
@@ -116,12 +120,16 @@ type Schema struct { | |||
116 | ForceNew bool | 120 | ForceNew bool |
117 | StateFunc SchemaStateFunc | 121 | StateFunc SchemaStateFunc |
118 | 122 | ||
119 | // The following fields are only set for a TypeList or TypeSet Type. | 123 | // The following fields are only set for a TypeList, TypeSet, or TypeMap. |
120 | // | 124 | // |
121 | // Elem must be either a *Schema or a *Resource only if the Type is | 125 | // Elem represents the element type. For a TypeMap, it must be a *Schema |
122 | // TypeList, and represents what the element type is. If it is *Schema, | 126 | // with a Type of TypeString, otherwise it may be either a *Schema or a |
123 | // the element type is just a simple value. If it is *Resource, the | 127 | // *Resource. If it is *Schema, the element type is just a simple value. |
124 | // element type is a complex structure, potentially with its own lifecycle. | 128 | // If it is *Resource, the element type is a complex structure, |
129 | // potentially with its own lifecycle. | ||
130 | Elem interface{} | ||
131 | |||
132 | // The following fields are only set for a TypeList or TypeSet. | ||
125 | // | 133 | // |
126 | // MaxItems defines a maximum amount of items that can exist within a | 134 | // MaxItems defines a maximum amount of items that can exist within a |
127 | // TypeSet or TypeList. Specific use cases would be if a TypeSet is being | 135 | // TypeSet or TypeList. Specific use cases would be if a TypeSet is being |
@@ -138,7 +146,6 @@ type Schema struct { | |||
138 | // ["foo"] automatically. This is primarily for legacy reasons and the | 146 | // ["foo"] automatically. This is primarily for legacy reasons and the |
139 | // ambiguity is not recommended for new usage. Promotion is only allowed | 147 | // ambiguity is not recommended for new usage. Promotion is only allowed |
140 | // for primitive element types. | 148 | // for primitive element types. |
141 | Elem interface{} | ||
142 | MaxItems int | 149 | MaxItems int |
143 | MinItems int | 150 | MinItems int |
144 | PromoteSingle bool | 151 | PromoteSingle bool |
@@ -192,7 +199,7 @@ type Schema struct { | |||
192 | Sensitive bool | 199 | Sensitive bool |
193 | } | 200 | } |
194 | 201 | ||
195 | // SchemaDiffSuppresFunc is a function which can be used to determine | 202 | // SchemaDiffSuppressFunc is a function which can be used to determine |
196 | // whether a detected diff on a schema element is "valid" or not, and | 203 | // whether a detected diff on a schema element is "valid" or not, and |
197 | // suppress it from the plan if necessary. | 204 | // suppress it from the plan if necessary. |
198 | // | 205 | // |
@@ -289,8 +296,7 @@ func (s *Schema) ZeroValue() interface{} { | |||
289 | } | 296 | } |
290 | } | 297 | } |
291 | 298 | ||
292 | func (s *Schema) finalizeDiff( | 299 | func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *terraform.ResourceAttrDiff { |
293 | d *terraform.ResourceAttrDiff) *terraform.ResourceAttrDiff { | ||
294 | if d == nil { | 300 | if d == nil { |
295 | return d | 301 | return d |
296 | } | 302 | } |
@@ -331,13 +337,20 @@ func (s *Schema) finalizeDiff( | |||
331 | } | 337 | } |
332 | 338 | ||
333 | if s.Computed { | 339 | if s.Computed { |
334 | if d.Old != "" && d.New == "" { | 340 | // FIXME: This is where the customized bool from getChange finally |
335 | // This is a computed value with an old value set already, | 341 | // comes into play. It allows the previously incorrect behavior |
336 | // just let it go. | 342 | // of an empty string being used as "unset" when the value is |
337 | return nil | 343 | // computed. This should be removed once we can properly |
344 | // represent an unset/nil value from the configuration. | ||
345 | if !customized { | ||
346 | if d.Old != "" && d.New == "" { | ||
347 | // This is a computed value with an old value set already, | ||
348 | // just let it go. | ||
349 | return nil | ||
350 | } | ||
338 | } | 351 | } |
339 | 352 | ||
340 | if d.New == "" { | 353 | if d.New == "" && !d.NewComputed { |
341 | // Computed attribute without a new value set | 354 | // Computed attribute without a new value set |
342 | d.NewComputed = true | 355 | d.NewComputed = true |
343 | } | 356 | } |
@@ -354,6 +367,13 @@ func (s *Schema) finalizeDiff( | |||
354 | // schemaMap is a wrapper that adds nice functions on top of schemas. | 367 | // schemaMap is a wrapper that adds nice functions on top of schemas. |
355 | type schemaMap map[string]*Schema | 368 | type schemaMap map[string]*Schema |
356 | 369 | ||
370 | func (m schemaMap) panicOnError() bool { | ||
371 | if os.Getenv(PanicOnErr) != "" { | ||
372 | return true | ||
373 | } | ||
374 | return false | ||
375 | } | ||
376 | |||
357 | // Data returns a ResourceData for the given schema, state, and diff. | 377 | // Data returns a ResourceData for the given schema, state, and diff. |
358 | // | 378 | // |
359 | // The diff is optional. | 379 | // The diff is optional. |
@@ -361,17 +381,30 @@ func (m schemaMap) Data( | |||
361 | s *terraform.InstanceState, | 381 | s *terraform.InstanceState, |
362 | d *terraform.InstanceDiff) (*ResourceData, error) { | 382 | d *terraform.InstanceDiff) (*ResourceData, error) { |
363 | return &ResourceData{ | 383 | return &ResourceData{ |
364 | schema: m, | 384 | schema: m, |
365 | state: s, | 385 | state: s, |
366 | diff: d, | 386 | diff: d, |
387 | panicOnError: m.panicOnError(), | ||
367 | }, nil | 388 | }, nil |
368 | } | 389 | } |
369 | 390 | ||
391 | // DeepCopy returns a copy of this schemaMap. The copy can be safely modified | ||
392 | // without affecting the original. | ||
393 | func (m *schemaMap) DeepCopy() schemaMap { | ||
394 | copy, err := copystructure.Config{Lock: true}.Copy(m) | ||
395 | if err != nil { | ||
396 | panic(err) | ||
397 | } | ||
398 | return *copy.(*schemaMap) | ||
399 | } | ||
400 | |||
370 | // Diff returns the diff for a resource given the schema map, | 401 | // Diff returns the diff for a resource given the schema map, |
371 | // state, and configuration. | 402 | // state, and configuration. |
372 | func (m schemaMap) Diff( | 403 | func (m schemaMap) Diff( |
373 | s *terraform.InstanceState, | 404 | s *terraform.InstanceState, |
374 | c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) { | 405 | c *terraform.ResourceConfig, |
406 | customizeDiff CustomizeDiffFunc, | ||
407 | meta interface{}) (*terraform.InstanceDiff, error) { | ||
375 | result := new(terraform.InstanceDiff) | 408 | result := new(terraform.InstanceDiff) |
376 | result.Attributes = make(map[string]*terraform.ResourceAttrDiff) | 409 | result.Attributes = make(map[string]*terraform.ResourceAttrDiff) |
377 | 410 | ||
@@ -381,9 +414,10 @@ func (m schemaMap) Diff( | |||
381 | } | 414 | } |
382 | 415 | ||
383 | d := &ResourceData{ | 416 | d := &ResourceData{ |
384 | schema: m, | 417 | schema: m, |
385 | state: s, | 418 | state: s, |
386 | config: c, | 419 | config: c, |
420 | panicOnError: m.panicOnError(), | ||
387 | } | 421 | } |
388 | 422 | ||
389 | for k, schema := range m { | 423 | for k, schema := range m { |
@@ -393,6 +427,29 @@ func (m schemaMap) Diff( | |||
393 | } | 427 | } |
394 | } | 428 | } |
395 | 429 | ||
430 | // Remove any nil diffs just to keep things clean | ||
431 | for k, v := range result.Attributes { | ||
432 | if v == nil { | ||
433 | delete(result.Attributes, k) | ||
434 | } | ||
435 | } | ||
436 | |||
437 | // If this is a non-destroy diff, call any custom diff logic that has been | ||
438 | // defined. | ||
439 | if !result.DestroyTainted && customizeDiff != nil { | ||
440 | mc := m.DeepCopy() | ||
441 | rd := newResourceDiff(mc, c, s, result) | ||
442 | if err := customizeDiff(rd, meta); err != nil { | ||
443 | return nil, err | ||
444 | } | ||
445 | for _, k := range rd.UpdatedKeys() { | ||
446 | err := m.diff(k, mc[k], result, rd, false) | ||
447 | if err != nil { | ||
448 | return nil, err | ||
449 | } | ||
450 | } | ||
451 | } | ||
452 | |||
396 | // If the diff requires a new resource, then we recompute the diff | 453 | // If the diff requires a new resource, then we recompute the diff |
397 | // so we have the complete new resource diff, and preserve the | 454 | // so we have the complete new resource diff, and preserve the |
398 | // RequiresNew fields where necessary so the user knows exactly what | 455 | // RequiresNew fields where necessary so the user knows exactly what |
@@ -418,6 +475,21 @@ func (m schemaMap) Diff( | |||
418 | } | 475 | } |
419 | } | 476 | } |
420 | 477 | ||
478 | // Re-run customization | ||
479 | if !result2.DestroyTainted && customizeDiff != nil { | ||
480 | mc := m.DeepCopy() | ||
481 | rd := newResourceDiff(mc, c, d.state, result2) | ||
482 | if err := customizeDiff(rd, meta); err != nil { | ||
483 | return nil, err | ||
484 | } | ||
485 | for _, k := range rd.UpdatedKeys() { | ||
486 | err := m.diff(k, mc[k], result2, rd, false) | ||
487 | if err != nil { | ||
488 | return nil, err | ||
489 | } | ||
490 | } | ||
491 | } | ||
492 | |||
421 | // Force all the fields to not force a new since we know what we | 493 | // Force all the fields to not force a new since we know what we |
422 | // want to force new. | 494 | // want to force new. |
423 | for k, attr := range result2.Attributes { | 495 | for k, attr := range result2.Attributes { |
@@ -456,13 +528,6 @@ func (m schemaMap) Diff( | |||
456 | result = result2 | 528 | result = result2 |
457 | } | 529 | } |
458 | 530 | ||
459 | // Remove any nil diffs just to keep things clean | ||
460 | for k, v := range result.Attributes { | ||
461 | if v == nil { | ||
462 | delete(result.Attributes, k) | ||
463 | } | ||
464 | } | ||
465 | |||
466 | // Go through and detect all of the ComputedWhens now that we've | 531 | // Go through and detect all of the ComputedWhens now that we've |
467 | // finished the diff. | 532 | // finished the diff. |
468 | // TODO | 533 | // TODO |
@@ -681,11 +746,23 @@ func isValidFieldName(name string) bool { | |||
681 | return re.MatchString(name) | 746 | return re.MatchString(name) |
682 | } | 747 | } |
683 | 748 | ||
749 | // resourceDiffer is an interface that is used by the private diff functions. | ||
750 | // This helps facilitate diff logic for both ResourceData and ResoureDiff with | ||
751 | // minimal divergence in code. | ||
752 | type resourceDiffer interface { | ||
753 | diffChange(string) (interface{}, interface{}, bool, bool, bool) | ||
754 | Get(string) interface{} | ||
755 | GetChange(string) (interface{}, interface{}) | ||
756 | GetOk(string) (interface{}, bool) | ||
757 | HasChange(string) bool | ||
758 | Id() string | ||
759 | } | ||
760 | |||
684 | func (m schemaMap) diff( | 761 | func (m schemaMap) diff( |
685 | k string, | 762 | k string, |
686 | schema *Schema, | 763 | schema *Schema, |
687 | diff *terraform.InstanceDiff, | 764 | diff *terraform.InstanceDiff, |
688 | d *ResourceData, | 765 | d resourceDiffer, |
689 | all bool) error { | 766 | all bool) error { |
690 | 767 | ||
691 | unsupressedDiff := new(terraform.InstanceDiff) | 768 | unsupressedDiff := new(terraform.InstanceDiff) |
@@ -706,12 +783,14 @@ func (m schemaMap) diff( | |||
706 | } | 783 | } |
707 | 784 | ||
708 | for attrK, attrV := range unsupressedDiff.Attributes { | 785 | for attrK, attrV := range unsupressedDiff.Attributes { |
709 | if schema.DiffSuppressFunc != nil && | 786 | switch rd := d.(type) { |
710 | attrV != nil && | 787 | case *ResourceData: |
711 | schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, d) { | 788 | if schema.DiffSuppressFunc != nil && |
712 | continue | 789 | attrV != nil && |
790 | schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, rd) { | ||
791 | continue | ||
792 | } | ||
713 | } | 793 | } |
714 | |||
715 | diff.Attributes[attrK] = attrV | 794 | diff.Attributes[attrK] = attrV |
716 | } | 795 | } |
717 | 796 | ||
@@ -722,9 +801,9 @@ func (m schemaMap) diffList( | |||
722 | k string, | 801 | k string, |
723 | schema *Schema, | 802 | schema *Schema, |
724 | diff *terraform.InstanceDiff, | 803 | diff *terraform.InstanceDiff, |
725 | d *ResourceData, | 804 | d resourceDiffer, |
726 | all bool) error { | 805 | all bool) error { |
727 | o, n, _, computedList := d.diffChange(k) | 806 | o, n, _, computedList, customized := d.diffChange(k) |
728 | if computedList { | 807 | if computedList { |
729 | n = nil | 808 | n = nil |
730 | } | 809 | } |
@@ -791,10 +870,13 @@ func (m schemaMap) diffList( | |||
791 | oldStr = "" | 870 | oldStr = "" |
792 | } | 871 | } |
793 | 872 | ||
794 | diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ | 873 | diff.Attributes[k+".#"] = countSchema.finalizeDiff( |
795 | Old: oldStr, | 874 | &terraform.ResourceAttrDiff{ |
796 | New: newStr, | 875 | Old: oldStr, |
797 | }) | 876 | New: newStr, |
877 | }, | ||
878 | customized, | ||
879 | ) | ||
798 | } | 880 | } |
799 | 881 | ||
800 | // Figure out the maximum | 882 | // Figure out the maximum |
@@ -841,13 +923,13 @@ func (m schemaMap) diffMap( | |||
841 | k string, | 923 | k string, |
842 | schema *Schema, | 924 | schema *Schema, |
843 | diff *terraform.InstanceDiff, | 925 | diff *terraform.InstanceDiff, |
844 | d *ResourceData, | 926 | d resourceDiffer, |
845 | all bool) error { | 927 | all bool) error { |
846 | prefix := k + "." | 928 | prefix := k + "." |
847 | 929 | ||
848 | // First get all the values from the state | 930 | // First get all the values from the state |
849 | var stateMap, configMap map[string]string | 931 | var stateMap, configMap map[string]string |
850 | o, n, _, nComputed := d.diffChange(k) | 932 | o, n, _, nComputed, customized := d.diffChange(k) |
851 | if err := mapstructure.WeakDecode(o, &stateMap); err != nil { | 933 | if err := mapstructure.WeakDecode(o, &stateMap); err != nil { |
852 | return fmt.Errorf("%s: %s", k, err) | 934 | return fmt.Errorf("%s: %s", k, err) |
853 | } | 935 | } |
@@ -899,6 +981,7 @@ func (m schemaMap) diffMap( | |||
899 | Old: oldStr, | 981 | Old: oldStr, |
900 | New: newStr, | 982 | New: newStr, |
901 | }, | 983 | }, |
984 | customized, | ||
902 | ) | 985 | ) |
903 | } | 986 | } |
904 | 987 | ||
@@ -916,16 +999,22 @@ func (m schemaMap) diffMap( | |||
916 | continue | 999 | continue |
917 | } | 1000 | } |
918 | 1001 | ||
919 | diff.Attributes[prefix+k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1002 | diff.Attributes[prefix+k] = schema.finalizeDiff( |
920 | Old: old, | 1003 | &terraform.ResourceAttrDiff{ |
921 | New: v, | 1004 | Old: old, |
922 | }) | 1005 | New: v, |
1006 | }, | ||
1007 | customized, | ||
1008 | ) | ||
923 | } | 1009 | } |
924 | for k, v := range stateMap { | 1010 | for k, v := range stateMap { |
925 | diff.Attributes[prefix+k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1011 | diff.Attributes[prefix+k] = schema.finalizeDiff( |
926 | Old: v, | 1012 | &terraform.ResourceAttrDiff{ |
927 | NewRemoved: true, | 1013 | Old: v, |
928 | }) | 1014 | NewRemoved: true, |
1015 | }, | ||
1016 | customized, | ||
1017 | ) | ||
929 | } | 1018 | } |
930 | 1019 | ||
931 | return nil | 1020 | return nil |
@@ -935,10 +1024,10 @@ func (m schemaMap) diffSet( | |||
935 | k string, | 1024 | k string, |
936 | schema *Schema, | 1025 | schema *Schema, |
937 | diff *terraform.InstanceDiff, | 1026 | diff *terraform.InstanceDiff, |
938 | d *ResourceData, | 1027 | d resourceDiffer, |
939 | all bool) error { | 1028 | all bool) error { |
940 | 1029 | ||
941 | o, n, _, computedSet := d.diffChange(k) | 1030 | o, n, _, computedSet, customized := d.diffChange(k) |
942 | if computedSet { | 1031 | if computedSet { |
943 | n = nil | 1032 | n = nil |
944 | } | 1033 | } |
@@ -997,20 +1086,26 @@ func (m schemaMap) diffSet( | |||
997 | countStr = "" | 1086 | countStr = "" |
998 | } | 1087 | } |
999 | 1088 | ||
1000 | diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1089 | diff.Attributes[k+".#"] = countSchema.finalizeDiff( |
1001 | Old: countStr, | 1090 | &terraform.ResourceAttrDiff{ |
1002 | NewComputed: true, | 1091 | Old: countStr, |
1003 | }) | 1092 | NewComputed: true, |
1093 | }, | ||
1094 | customized, | ||
1095 | ) | ||
1004 | return nil | 1096 | return nil |
1005 | } | 1097 | } |
1006 | 1098 | ||
1007 | // If the counts are not the same, then record that diff | 1099 | // If the counts are not the same, then record that diff |
1008 | changed := oldLen != newLen | 1100 | changed := oldLen != newLen |
1009 | if changed || all { | 1101 | if changed || all { |
1010 | diff.Attributes[k+".#"] = countSchema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1102 | diff.Attributes[k+".#"] = countSchema.finalizeDiff( |
1011 | Old: oldStr, | 1103 | &terraform.ResourceAttrDiff{ |
1012 | New: newStr, | 1104 | Old: oldStr, |
1013 | }) | 1105 | New: newStr, |
1106 | }, | ||
1107 | customized, | ||
1108 | ) | ||
1014 | } | 1109 | } |
1015 | 1110 | ||
1016 | // Build the list of codes that will make up our set. This is the | 1111 | // Build the list of codes that will make up our set. This is the |
@@ -1056,11 +1151,11 @@ func (m schemaMap) diffString( | |||
1056 | k string, | 1151 | k string, |
1057 | schema *Schema, | 1152 | schema *Schema, |
1058 | diff *terraform.InstanceDiff, | 1153 | diff *terraform.InstanceDiff, |
1059 | d *ResourceData, | 1154 | d resourceDiffer, |
1060 | all bool) error { | 1155 | all bool) error { |
1061 | var originalN interface{} | 1156 | var originalN interface{} |
1062 | var os, ns string | 1157 | var os, ns string |
1063 | o, n, _, computed := d.diffChange(k) | 1158 | o, n, _, computed, customized := d.diffChange(k) |
1064 | if schema.StateFunc != nil && n != nil { | 1159 | if schema.StateFunc != nil && n != nil { |
1065 | originalN = n | 1160 | originalN = n |
1066 | n = schema.StateFunc(n) | 1161 | n = schema.StateFunc(n) |
@@ -1090,20 +1185,23 @@ func (m schemaMap) diffString( | |||
1090 | } | 1185 | } |
1091 | 1186 | ||
1092 | removed := false | 1187 | removed := false |
1093 | if o != nil && n == nil { | 1188 | if o != nil && n == nil && !computed { |
1094 | removed = true | 1189 | removed = true |
1095 | } | 1190 | } |
1096 | if removed && schema.Computed { | 1191 | if removed && schema.Computed { |
1097 | return nil | 1192 | return nil |
1098 | } | 1193 | } |
1099 | 1194 | ||
1100 | diff.Attributes[k] = schema.finalizeDiff(&terraform.ResourceAttrDiff{ | 1195 | diff.Attributes[k] = schema.finalizeDiff( |
1101 | Old: os, | 1196 | &terraform.ResourceAttrDiff{ |
1102 | New: ns, | 1197 | Old: os, |
1103 | NewExtra: originalN, | 1198 | New: ns, |
1104 | NewRemoved: removed, | 1199 | NewExtra: originalN, |
1105 | NewComputed: computed, | 1200 | NewRemoved: removed, |
1106 | }) | 1201 | NewComputed: computed, |
1202 | }, | ||
1203 | customized, | ||
1204 | ) | ||
1107 | 1205 | ||
1108 | return nil | 1206 | return nil |
1109 | } | 1207 | } |
@@ -1172,9 +1270,9 @@ func (m schemaMap) validateConflictingAttributes( | |||
1172 | } | 1270 | } |
1173 | 1271 | ||
1174 | for _, conflicting_key := range schema.ConflictsWith { | 1272 | for _, conflicting_key := range schema.ConflictsWith { |
1175 | if value, ok := c.Get(conflicting_key); ok { | 1273 | if _, ok := c.Get(conflicting_key); ok { |
1176 | return fmt.Errorf( | 1274 | return fmt.Errorf( |
1177 | "%q: conflicts with %s (%#v)", k, conflicting_key, value) | 1275 | "%q: conflicts with %s", k, conflicting_key) |
1178 | } | 1276 | } |
1179 | } | 1277 | } |
1180 | 1278 | ||
@@ -1363,13 +1461,10 @@ func getValueType(k string, schema *Schema) (ValueType, error) { | |||
1363 | return vt, nil | 1461 | return vt, nil |
1364 | } | 1462 | } |
1365 | 1463 | ||
1464 | // If a Schema is provided to a Map, we use the Type of that schema | ||
1465 | // as the type for each element in the Map. | ||
1366 | if s, ok := schema.Elem.(*Schema); ok { | 1466 | if s, ok := schema.Elem.(*Schema); ok { |
1367 | if s.Elem == nil { | 1467 | return s.Type, nil |
1368 | return TypeString, nil | ||
1369 | } | ||
1370 | if vt, ok := s.Elem.(ValueType); ok { | ||
1371 | return vt, nil | ||
1372 | } | ||
1373 | } | 1468 | } |
1374 | 1469 | ||
1375 | if _, ok := schema.Elem.(*Resource); ok { | 1470 | if _, ok := schema.Elem.(*Resource); ok { |
@@ -1430,7 +1525,6 @@ func (m schemaMap) validatePrimitive( | |||
1430 | raw interface{}, | 1525 | raw interface{}, |
1431 | schema *Schema, | 1526 | schema *Schema, |
1432 | c *terraform.ResourceConfig) ([]string, []error) { | 1527 | c *terraform.ResourceConfig) ([]string, []error) { |
1433 | |||
1434 | // Catch if the user gave a complex type where a primitive was | 1528 | // Catch if the user gave a complex type where a primitive was |
1435 | // expected, so we can return a friendly error message that | 1529 | // expected, so we can return a friendly error message that |
1436 | // doesn't contain Go type system terminology. | 1530 | // doesn't contain Go type system terminology. |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/set.go b/vendor/github.com/hashicorp/terraform/helper/schema/set.go index de05f40..cba2890 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/set.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/set.go | |||
@@ -17,6 +17,12 @@ func HashString(v interface{}) int { | |||
17 | return hashcode.String(v.(string)) | 17 | return hashcode.String(v.(string)) |
18 | } | 18 | } |
19 | 19 | ||
20 | // HashInt hashes integers. If you want a Set of integers, this is the | ||
21 | // SchemaSetFunc you want. | ||
22 | func HashInt(v interface{}) int { | ||
23 | return hashcode.String(strconv.Itoa(v.(int))) | ||
24 | } | ||
25 | |||
20 | // HashResource hashes complex structures that are described using | 26 | // HashResource hashes complex structures that are described using |
21 | // a *Resource. This is the default set implementation used when a set's | 27 | // a *Resource. This is the default set implementation used when a set's |
22 | // element type is a full resource. | 28 | // element type is a full resource. |
@@ -153,6 +159,31 @@ func (s *Set) Equal(raw interface{}) bool { | |||
153 | return reflect.DeepEqual(s.m, other.m) | 159 | return reflect.DeepEqual(s.m, other.m) |
154 | } | 160 | } |
155 | 161 | ||
162 | // HashEqual simply checks to the keys the top-level map to the keys in the | ||
163 | // other set's top-level map to see if they are equal. This obviously assumes | ||
164 | // you have a properly working hash function - use HashResource if in doubt. | ||
165 | func (s *Set) HashEqual(raw interface{}) bool { | ||
166 | other, ok := raw.(*Set) | ||
167 | if !ok { | ||
168 | return false | ||
169 | } | ||
170 | |||
171 | ks1 := make([]string, 0) | ||
172 | ks2 := make([]string, 0) | ||
173 | |||
174 | for k := range s.m { | ||
175 | ks1 = append(ks1, k) | ||
176 | } | ||
177 | for k := range other.m { | ||
178 | ks2 = append(ks2, k) | ||
179 | } | ||
180 | |||
181 | sort.Strings(ks1) | ||
182 | sort.Strings(ks2) | ||
183 | |||
184 | return reflect.DeepEqual(ks1, ks2) | ||
185 | } | ||
186 | |||
156 | func (s *Set) GoString() string { | 187 | func (s *Set) GoString() string { |
157 | return fmt.Sprintf("*Set(%#v)", s.m) | 188 | return fmt.Sprintf("*Set(%#v)", s.m) |
158 | } | 189 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go index 9765bdb..da754ac 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/testing.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/testing.go | |||
@@ -10,13 +10,15 @@ import ( | |||
10 | // TestResourceDataRaw creates a ResourceData from a raw configuration map. | 10 | // TestResourceDataRaw creates a ResourceData from a raw configuration map. |
11 | func TestResourceDataRaw( | 11 | func TestResourceDataRaw( |
12 | t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData { | 12 | t *testing.T, schema map[string]*Schema, raw map[string]interface{}) *ResourceData { |
13 | t.Helper() | ||
14 | |||
13 | c, err := config.NewRawConfig(raw) | 15 | c, err := config.NewRawConfig(raw) |
14 | if err != nil { | 16 | if err != nil { |
15 | t.Fatalf("err: %s", err) | 17 | t.Fatalf("err: %s", err) |
16 | } | 18 | } |
17 | 19 | ||
18 | sm := schemaMap(schema) | 20 | sm := schemaMap(schema) |
19 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c)) | 21 | diff, err := sm.Diff(nil, terraform.NewResourceConfig(c), nil, nil) |
20 | if err != nil { | 22 | if err != nil { |
21 | t.Fatalf("err: %s", err) | 23 | t.Fatalf("err: %s", err) |
22 | } | 24 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go index 1610cec..3bc3ac4 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/valuetype_string.go | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | package schema | 3 | package schema |
4 | 4 | ||
5 | import "fmt" | 5 | import "strconv" |
6 | 6 | ||
7 | const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject" | 7 | const _ValueType_name = "TypeInvalidTypeBoolTypeIntTypeFloatTypeStringTypeListTypeMapTypeSettypeObject" |
8 | 8 | ||
@@ -10,7 +10,7 @@ var _ValueType_index = [...]uint8{0, 11, 19, 26, 35, 45, 53, 60, 67, 77} | |||
10 | 10 | ||
11 | func (i ValueType) String() string { | 11 | func (i ValueType) String() string { |
12 | if i < 0 || i >= ValueType(len(_ValueType_index)-1) { | 12 | if i < 0 || i >= ValueType(len(_ValueType_index)-1) { |
13 | return fmt.Sprintf("ValueType(%d)", i) | 13 | return "ValueType(" + strconv.FormatInt(int64(i), 10) + ")" |
14 | } | 14 | } |
15 | return _ValueType_name[_ValueType_index[i]:_ValueType_index[i+1]] | 15 | return _ValueType_name[_ValueType_index[i]:_ValueType_index[i+1]] |
16 | } | 16 | } |
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/closer.go b/vendor/github.com/hashicorp/terraform/helper/shadow/closer.go deleted file mode 100644 index edc1e2a..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/shadow/closer.go +++ /dev/null | |||
@@ -1,83 +0,0 @@ | |||
1 | package shadow | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "io" | ||
6 | "reflect" | ||
7 | |||
8 | "github.com/hashicorp/go-multierror" | ||
9 | "github.com/mitchellh/reflectwalk" | ||
10 | ) | ||
11 | |||
12 | // Close will close all shadow values within the given structure. | ||
13 | // | ||
14 | // This uses reflection to walk the structure, find all shadow elements, | ||
15 | // and close them. Currently this will only find struct fields that are | ||
16 | // shadow values, and not slice elements, etc. | ||
17 | func Close(v interface{}) error { | ||
18 | // We require a pointer so we can address the internal fields | ||
19 | val := reflect.ValueOf(v) | ||
20 | if val.Kind() != reflect.Ptr { | ||
21 | return fmt.Errorf("value must be a pointer") | ||
22 | } | ||
23 | |||
24 | // Walk and close | ||
25 | var w closeWalker | ||
26 | if err := reflectwalk.Walk(v, &w); err != nil { | ||
27 | return err | ||
28 | } | ||
29 | |||
30 | return w.Err | ||
31 | } | ||
32 | |||
33 | type closeWalker struct { | ||
34 | Err error | ||
35 | } | ||
36 | |||
37 | func (w *closeWalker) Struct(reflect.Value) error { | ||
38 | // Do nothing. We implement this for reflectwalk.StructWalker | ||
39 | return nil | ||
40 | } | ||
41 | |||
42 | var closerType = reflect.TypeOf((*io.Closer)(nil)).Elem() | ||
43 | |||
44 | func (w *closeWalker) StructField(f reflect.StructField, v reflect.Value) error { | ||
45 | // Not sure why this would be but lets avoid some panics | ||
46 | if !v.IsValid() { | ||
47 | return nil | ||
48 | } | ||
49 | |||
50 | // Empty for exported, so don't check unexported fields | ||
51 | if f.PkgPath != "" { | ||
52 | return nil | ||
53 | } | ||
54 | |||
55 | // Verify the io.Closer is in this package | ||
56 | typ := v.Type() | ||
57 | if typ.PkgPath() != "github.com/hashicorp/terraform/helper/shadow" { | ||
58 | return nil | ||
59 | } | ||
60 | |||
61 | var closer io.Closer | ||
62 | if v.Type().Implements(closerType) { | ||
63 | closer = v.Interface().(io.Closer) | ||
64 | } else if v.CanAddr() { | ||
65 | // The Close method may require a pointer receiver, but we only have a value. | ||
66 | v := v.Addr() | ||
67 | if v.Type().Implements(closerType) { | ||
68 | closer = v.Interface().(io.Closer) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | if closer == nil { | ||
73 | return reflectwalk.SkipEntry | ||
74 | } | ||
75 | |||
76 | // Close it | ||
77 | if err := closer.Close(); err != nil { | ||
78 | w.Err = multierror.Append(w.Err, err) | ||
79 | } | ||
80 | |||
81 | // Don't go into the struct field | ||
82 | return reflectwalk.SkipEntry | ||
83 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go deleted file mode 100644 index 4223e92..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go +++ /dev/null | |||
@@ -1,128 +0,0 @@ | |||
1 | package shadow | ||
2 | |||
3 | import ( | ||
4 | "sync" | ||
5 | ) | ||
6 | |||
7 | // ComparedValue is a struct that finds a value by comparing some key | ||
8 | // to the list of stored values. This is useful when there is no easy | ||
9 | // uniquely identifying key that works in a map (for that, use KeyedValue). | ||
10 | // | ||
11 | // ComparedValue is very expensive, relative to other Value types. Try to | ||
12 | // limit the number of values stored in a ComparedValue by potentially | ||
13 | // nesting it within a KeyedValue (a keyed value points to a compared value, | ||
14 | // for example). | ||
15 | type ComparedValue struct { | ||
16 | // Func is a function that is given the lookup key and a single | ||
17 | // stored value. If it matches, it returns true. | ||
18 | Func func(k, v interface{}) bool | ||
19 | |||
20 | lock sync.Mutex | ||
21 | once sync.Once | ||
22 | closed bool | ||
23 | values []interface{} | ||
24 | waiters map[interface{}]*Value | ||
25 | } | ||
26 | |||
27 | // Close closes the value. This can never fail. For a definition of | ||
28 | // "close" see the ErrClosed docs. | ||
29 | func (w *ComparedValue) Close() error { | ||
30 | w.lock.Lock() | ||
31 | defer w.lock.Unlock() | ||
32 | |||
33 | // Set closed to true always | ||
34 | w.closed = true | ||
35 | |||
36 | // For all waiters, complete with ErrClosed | ||
37 | for k, val := range w.waiters { | ||
38 | val.SetValue(ErrClosed) | ||
39 | delete(w.waiters, k) | ||
40 | } | ||
41 | |||
42 | return nil | ||
43 | } | ||
44 | |||
45 | // Value returns the value that was set for the given key, or blocks | ||
46 | // until one is available. | ||
47 | func (w *ComparedValue) Value(k interface{}) interface{} { | ||
48 | v, val := w.valueWaiter(k) | ||
49 | if val == nil { | ||
50 | return v | ||
51 | } | ||
52 | |||
53 | return val.Value() | ||
54 | } | ||
55 | |||
56 | // ValueOk gets the value for the given key, returning immediately if the | ||
57 | // value doesn't exist. The second return argument is true if the value exists. | ||
58 | func (w *ComparedValue) ValueOk(k interface{}) (interface{}, bool) { | ||
59 | v, val := w.valueWaiter(k) | ||
60 | return v, val == nil | ||
61 | } | ||
62 | |||
63 | func (w *ComparedValue) SetValue(v interface{}) { | ||
64 | w.lock.Lock() | ||
65 | defer w.lock.Unlock() | ||
66 | w.once.Do(w.init) | ||
67 | |||
68 | // Check if we already have this exact value (by simply comparing | ||
69 | // with == directly). If we do, then we don't insert it again. | ||
70 | found := false | ||
71 | for _, v2 := range w.values { | ||
72 | if v == v2 { | ||
73 | found = true | ||
74 | break | ||
75 | } | ||
76 | } | ||
77 | |||
78 | if !found { | ||
79 | // Set the value, always | ||
80 | w.values = append(w.values, v) | ||
81 | } | ||
82 | |||
83 | // Go through the waiters | ||
84 | for k, val := range w.waiters { | ||
85 | if w.Func(k, v) { | ||
86 | val.SetValue(v) | ||
87 | delete(w.waiters, k) | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | |||
92 | func (w *ComparedValue) valueWaiter(k interface{}) (interface{}, *Value) { | ||
93 | w.lock.Lock() | ||
94 | w.once.Do(w.init) | ||
95 | |||
96 | // Look for a pre-existing value | ||
97 | for _, v := range w.values { | ||
98 | if w.Func(k, v) { | ||
99 | w.lock.Unlock() | ||
100 | return v, nil | ||
101 | } | ||
102 | } | ||
103 | |||
104 | // If we're closed, return that | ||
105 | if w.closed { | ||
106 | w.lock.Unlock() | ||
107 | return ErrClosed, nil | ||
108 | } | ||
109 | |||
110 | // Pre-existing value doesn't exist, create a waiter | ||
111 | val := w.waiters[k] | ||
112 | if val == nil { | ||
113 | val = new(Value) | ||
114 | w.waiters[k] = val | ||
115 | } | ||
116 | w.lock.Unlock() | ||
117 | |||
118 | // Return the waiter | ||
119 | return nil, val | ||
120 | } | ||
121 | |||
122 | // Must be called with w.lock held. | ||
123 | func (w *ComparedValue) init() { | ||
124 | w.waiters = make(map[interface{}]*Value) | ||
125 | if w.Func == nil { | ||
126 | w.Func = func(k, v interface{}) bool { return k == v } | ||
127 | } | ||
128 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/keyed_value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/keyed_value.go deleted file mode 100644 index 432b036..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/shadow/keyed_value.go +++ /dev/null | |||
@@ -1,151 +0,0 @@ | |||
1 | package shadow | ||
2 | |||
3 | import ( | ||
4 | "sync" | ||
5 | ) | ||
6 | |||
7 | // KeyedValue is a struct that coordinates a value by key. If a value is | ||
8 | // not available for a give key, it'll block until it is available. | ||
9 | type KeyedValue struct { | ||
10 | lock sync.Mutex | ||
11 | once sync.Once | ||
12 | values map[string]interface{} | ||
13 | waiters map[string]*Value | ||
14 | closed bool | ||
15 | } | ||
16 | |||
17 | // Close closes the value. This can never fail. For a definition of | ||
18 | // "close" see the ErrClosed docs. | ||
19 | func (w *KeyedValue) Close() error { | ||
20 | w.lock.Lock() | ||
21 | defer w.lock.Unlock() | ||
22 | |||
23 | // Set closed to true always | ||
24 | w.closed = true | ||
25 | |||
26 | // For all waiters, complete with ErrClosed | ||
27 | for k, val := range w.waiters { | ||
28 | val.SetValue(ErrClosed) | ||
29 | delete(w.waiters, k) | ||
30 | } | ||
31 | |||
32 | return nil | ||
33 | } | ||
34 | |||
35 | // Value returns the value that was set for the given key, or blocks | ||
36 | // until one is available. | ||
37 | func (w *KeyedValue) Value(k string) interface{} { | ||
38 | w.lock.Lock() | ||
39 | v, val := w.valueWaiter(k) | ||
40 | w.lock.Unlock() | ||
41 | |||
42 | // If we have no waiter, then return the value | ||
43 | if val == nil { | ||
44 | return v | ||
45 | } | ||
46 | |||
47 | // We have a waiter, so wait | ||
48 | return val.Value() | ||
49 | } | ||
50 | |||
51 | // WaitForChange waits for the value with the given key to be set again. | ||
52 | // If the key isn't set, it'll wait for an initial value. Note that while | ||
53 | // it is called "WaitForChange", the value isn't guaranteed to _change_; | ||
54 | // this will return when a SetValue is called for the given k. | ||
55 | func (w *KeyedValue) WaitForChange(k string) interface{} { | ||
56 | w.lock.Lock() | ||
57 | w.once.Do(w.init) | ||
58 | |||
59 | // If we're closed, we're closed | ||
60 | if w.closed { | ||
61 | w.lock.Unlock() | ||
62 | return ErrClosed | ||
63 | } | ||
64 | |||
65 | // Check for an active waiter. If there isn't one, make it | ||
66 | val := w.waiters[k] | ||
67 | if val == nil { | ||
68 | val = new(Value) | ||
69 | w.waiters[k] = val | ||
70 | } | ||
71 | w.lock.Unlock() | ||
72 | |||
73 | // And wait | ||
74 | return val.Value() | ||
75 | } | ||
76 | |||
77 | // ValueOk gets the value for the given key, returning immediately if the | ||
78 | // value doesn't exist. The second return argument is true if the value exists. | ||
79 | func (w *KeyedValue) ValueOk(k string) (interface{}, bool) { | ||
80 | w.lock.Lock() | ||
81 | defer w.lock.Unlock() | ||
82 | |||
83 | v, val := w.valueWaiter(k) | ||
84 | return v, val == nil | ||
85 | } | ||
86 | |||
87 | func (w *KeyedValue) SetValue(k string, v interface{}) { | ||
88 | w.lock.Lock() | ||
89 | defer w.lock.Unlock() | ||
90 | w.setValue(k, v) | ||
91 | } | ||
92 | |||
93 | // Init will initialize the key to a given value only if the key has | ||
94 | // not been set before. This is safe to call multiple times and in parallel. | ||
95 | func (w *KeyedValue) Init(k string, v interface{}) { | ||
96 | w.lock.Lock() | ||
97 | defer w.lock.Unlock() | ||
98 | |||
99 | // If we have a waiter, set the value. | ||
100 | _, val := w.valueWaiter(k) | ||
101 | if val != nil { | ||
102 | w.setValue(k, v) | ||
103 | } | ||
104 | } | ||
105 | |||
106 | // Must be called with w.lock held. | ||
107 | func (w *KeyedValue) init() { | ||
108 | w.values = make(map[string]interface{}) | ||
109 | w.waiters = make(map[string]*Value) | ||
110 | } | ||
111 | |||
112 | // setValue is like SetValue but assumes the lock is held. | ||
113 | func (w *KeyedValue) setValue(k string, v interface{}) { | ||
114 | w.once.Do(w.init) | ||
115 | |||
116 | // Set the value, always | ||
117 | w.values[k] = v | ||
118 | |||
119 | // If we have a waiter, set it | ||
120 | if val, ok := w.waiters[k]; ok { | ||
121 | val.SetValue(v) | ||
122 | delete(w.waiters, k) | ||
123 | } | ||
124 | } | ||
125 | |||
126 | // valueWaiter gets the value or the Value waiter for a given key. | ||
127 | // | ||
128 | // This must be called with lock held. | ||
129 | func (w *KeyedValue) valueWaiter(k string) (interface{}, *Value) { | ||
130 | w.once.Do(w.init) | ||
131 | |||
132 | // If we have this value already, return it | ||
133 | if v, ok := w.values[k]; ok { | ||
134 | return v, nil | ||
135 | } | ||
136 | |||
137 | // If we're closed, return that | ||
138 | if w.closed { | ||
139 | return ErrClosed, nil | ||
140 | } | ||
141 | |||
142 | // No pending value, check for a waiter | ||
143 | val := w.waiters[k] | ||
144 | if val == nil { | ||
145 | val = new(Value) | ||
146 | w.waiters[k] = val | ||
147 | } | ||
148 | |||
149 | // Return the waiter | ||
150 | return nil, val | ||
151 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/ordered_value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/ordered_value.go deleted file mode 100644 index 0a43d4d..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/shadow/ordered_value.go +++ /dev/null | |||
@@ -1,66 +0,0 @@ | |||
1 | package shadow | ||
2 | |||
3 | import ( | ||
4 | "container/list" | ||
5 | "sync" | ||
6 | ) | ||
7 | |||
8 | // OrderedValue is a struct that keeps track of a value in the order | ||
9 | // it is set. Each time Value() is called, it will return the most recent | ||
10 | // calls value then discard it. | ||
11 | // | ||
12 | // This is unlike Value that returns the same value once it is set. | ||
13 | type OrderedValue struct { | ||
14 | lock sync.Mutex | ||
15 | values *list.List | ||
16 | waiters *list.List | ||
17 | } | ||
18 | |||
19 | // Value returns the last value that was set, or blocks until one | ||
20 | // is received. | ||
21 | func (w *OrderedValue) Value() interface{} { | ||
22 | w.lock.Lock() | ||
23 | |||
24 | // If we have a pending value already, use it | ||
25 | if w.values != nil && w.values.Len() > 0 { | ||
26 | front := w.values.Front() | ||
27 | w.values.Remove(front) | ||
28 | w.lock.Unlock() | ||
29 | return front.Value | ||
30 | } | ||
31 | |||
32 | // No pending value, create a waiter | ||
33 | if w.waiters == nil { | ||
34 | w.waiters = list.New() | ||
35 | } | ||
36 | |||
37 | var val Value | ||
38 | w.waiters.PushBack(&val) | ||
39 | w.lock.Unlock() | ||
40 | |||
41 | // Return the value once we have it | ||
42 | return val.Value() | ||
43 | } | ||
44 | |||
45 | // SetValue sets the latest value. | ||
46 | func (w *OrderedValue) SetValue(v interface{}) { | ||
47 | w.lock.Lock() | ||
48 | defer w.lock.Unlock() | ||
49 | |||
50 | // If we have a waiter, notify it | ||
51 | if w.waiters != nil && w.waiters.Len() > 0 { | ||
52 | front := w.waiters.Front() | ||
53 | w.waiters.Remove(front) | ||
54 | |||
55 | val := front.Value.(*Value) | ||
56 | val.SetValue(v) | ||
57 | return | ||
58 | } | ||
59 | |||
60 | // Add it to the list of values | ||
61 | if w.values == nil { | ||
62 | w.values = list.New() | ||
63 | } | ||
64 | |||
65 | w.values.PushBack(v) | ||
66 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/value.go deleted file mode 100644 index 178b7e7..0000000 --- a/vendor/github.com/hashicorp/terraform/helper/shadow/value.go +++ /dev/null | |||
@@ -1,87 +0,0 @@ | |||
1 | package shadow | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "sync" | ||
6 | ) | ||
7 | |||
8 | // ErrClosed is returned by any closed values. | ||
9 | // | ||
10 | // A "closed value" is when the shadow has been notified that the real | ||
11 | // side is complete and any blocking values will _never_ be satisfied | ||
12 | // in the future. In this case, this error is returned. If a value is already | ||
13 | // available, that is still returned. | ||
14 | var ErrClosed = errors.New("shadow closed") | ||
15 | |||
16 | // Value is a struct that coordinates a value between two | ||
17 | // parallel routines. It is similar to atomic.Value except that when | ||
18 | // Value is called if it isn't set it will wait for it. | ||
19 | // | ||
20 | // The Value can be closed with Close, which will cause any future | ||
21 | // blocking operations to return immediately with ErrClosed. | ||
22 | type Value struct { | ||
23 | lock sync.Mutex | ||
24 | cond *sync.Cond | ||
25 | value interface{} | ||
26 | valueSet bool | ||
27 | } | ||
28 | |||
29 | func (v *Value) Lock() { | ||
30 | v.lock.Lock() | ||
31 | } | ||
32 | |||
33 | func (v *Value) Unlock() { | ||
34 | v.lock.Unlock() | ||
35 | } | ||
36 | |||
37 | // Close closes the value. This can never fail. For a definition of | ||
38 | // "close" see the struct docs. | ||
39 | func (w *Value) Close() error { | ||
40 | w.lock.Lock() | ||
41 | set := w.valueSet | ||
42 | w.lock.Unlock() | ||
43 | |||
44 | // If we haven't set the value, set it | ||
45 | if !set { | ||
46 | w.SetValue(ErrClosed) | ||
47 | } | ||
48 | |||
49 | // Done | ||
50 | return nil | ||
51 | } | ||
52 | |||
53 | // Value returns the value that was set. | ||
54 | func (w *Value) Value() interface{} { | ||
55 | w.lock.Lock() | ||
56 | defer w.lock.Unlock() | ||
57 | |||
58 | // If we already have a value just return | ||
59 | for !w.valueSet { | ||
60 | // No value, setup the condition variable if we have to | ||
61 | if w.cond == nil { | ||
62 | w.cond = sync.NewCond(&w.lock) | ||
63 | } | ||
64 | |||
65 | // Wait on it | ||
66 | w.cond.Wait() | ||
67 | } | ||
68 | |||
69 | // Return the value | ||
70 | return w.value | ||
71 | } | ||
72 | |||
73 | // SetValue sets the value. | ||
74 | func (w *Value) SetValue(v interface{}) { | ||
75 | w.lock.Lock() | ||
76 | defer w.lock.Unlock() | ||
77 | |||
78 | // Set the value | ||
79 | w.valueSet = true | ||
80 | w.value = v | ||
81 | |||
82 | // If we have a condition, clear it | ||
83 | if w.cond != nil { | ||
84 | w.cond.Broadcast() | ||
85 | w.cond = nil | ||
86 | } | ||
87 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/httpclient/client.go b/vendor/github.com/hashicorp/terraform/httpclient/client.go new file mode 100644 index 0000000..bb06beb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/httpclient/client.go | |||
@@ -0,0 +1,18 @@ | |||
1 | package httpclient | ||
2 | |||
3 | import ( | ||
4 | "net/http" | ||
5 | |||
6 | cleanhttp "github.com/hashicorp/go-cleanhttp" | ||
7 | ) | ||
8 | |||
9 | // New returns the DefaultPooledClient from the cleanhttp | ||
10 | // package that will also send a Terraform User-Agent string. | ||
11 | func New() *http.Client { | ||
12 | cli := cleanhttp.DefaultPooledClient() | ||
13 | cli.Transport = &userAgentRoundTripper{ | ||
14 | userAgent: UserAgentString(), | ||
15 | inner: cli.Transport, | ||
16 | } | ||
17 | return cli | ||
18 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/httpclient/useragent.go b/vendor/github.com/hashicorp/terraform/httpclient/useragent.go new file mode 100644 index 0000000..5e28017 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/httpclient/useragent.go | |||
@@ -0,0 +1,40 @@ | |||
1 | package httpclient | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "log" | ||
6 | "net/http" | ||
7 | "os" | ||
8 | "strings" | ||
9 | |||
10 | "github.com/hashicorp/terraform/version" | ||
11 | ) | ||
12 | |||
13 | const userAgentFormat = "Terraform/%s" | ||
14 | const uaEnvVar = "TF_APPEND_USER_AGENT" | ||
15 | |||
16 | func UserAgentString() string { | ||
17 | ua := fmt.Sprintf(userAgentFormat, version.Version) | ||
18 | |||
19 | if add := os.Getenv(uaEnvVar); add != "" { | ||
20 | add = strings.TrimSpace(add) | ||
21 | if len(add) > 0 { | ||
22 | ua += " " + add | ||
23 | log.Printf("[DEBUG] Using modified User-Agent: %s", ua) | ||
24 | } | ||
25 | } | ||
26 | |||
27 | return ua | ||
28 | } | ||
29 | |||
30 | type userAgentRoundTripper struct { | ||
31 | inner http.RoundTripper | ||
32 | userAgent string | ||
33 | } | ||
34 | |||
35 | func (rt *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { | ||
36 | if _, ok := req.Header["User-Agent"]; !ok { | ||
37 | req.Header.Set("User-Agent", rt.userAgent) | ||
38 | } | ||
39 | return rt.inner.RoundTrip(req) | ||
40 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/plugin/client.go b/vendor/github.com/hashicorp/terraform/plugin/client.go index 3a5cb7a..7e2f4fe 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/client.go +++ b/vendor/github.com/hashicorp/terraform/plugin/client.go | |||
@@ -1,8 +1,10 @@ | |||
1 | package plugin | 1 | package plugin |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "os" | ||
4 | "os/exec" | 5 | "os/exec" |
5 | 6 | ||
7 | hclog "github.com/hashicorp/go-hclog" | ||
6 | plugin "github.com/hashicorp/go-plugin" | 8 | plugin "github.com/hashicorp/go-plugin" |
7 | "github.com/hashicorp/terraform/plugin/discovery" | 9 | "github.com/hashicorp/terraform/plugin/discovery" |
8 | ) | 10 | ) |
@@ -10,11 +12,18 @@ import ( | |||
10 | // ClientConfig returns a configuration object that can be used to instantiate | 12 | // ClientConfig returns a configuration object that can be used to instantiate |
11 | // a client for the plugin described by the given metadata. | 13 | // a client for the plugin described by the given metadata. |
12 | func ClientConfig(m discovery.PluginMeta) *plugin.ClientConfig { | 14 | func ClientConfig(m discovery.PluginMeta) *plugin.ClientConfig { |
15 | logger := hclog.New(&hclog.LoggerOptions{ | ||
16 | Name: "plugin", | ||
17 | Level: hclog.Trace, | ||
18 | Output: os.Stderr, | ||
19 | }) | ||
20 | |||
13 | return &plugin.ClientConfig{ | 21 | return &plugin.ClientConfig{ |
14 | Cmd: exec.Command(m.Path), | 22 | Cmd: exec.Command(m.Path), |
15 | HandshakeConfig: Handshake, | 23 | HandshakeConfig: Handshake, |
16 | Managed: true, | 24 | Managed: true, |
17 | Plugins: PluginMap, | 25 | Plugins: PluginMap, |
26 | Logger: logger, | ||
18 | } | 27 | } |
19 | } | 28 | } |
20 | 29 | ||
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go index f5bc4c1..f053312 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go +++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/find.go | |||
@@ -3,6 +3,7 @@ package discovery | |||
3 | import ( | 3 | import ( |
4 | "io/ioutil" | 4 | "io/ioutil" |
5 | "log" | 5 | "log" |
6 | "os" | ||
6 | "path/filepath" | 7 | "path/filepath" |
7 | "strings" | 8 | "strings" |
8 | ) | 9 | ) |
@@ -59,7 +60,6 @@ func findPluginPaths(kind string, dirs []string) []string { | |||
59 | fullName := item.Name() | 60 | fullName := item.Name() |
60 | 61 | ||
61 | if !strings.HasPrefix(fullName, prefix) { | 62 | if !strings.HasPrefix(fullName, prefix) { |
62 | log.Printf("[DEBUG] skipping %q, not a %s", fullName, kind) | ||
63 | continue | 63 | continue |
64 | } | 64 | } |
65 | 65 | ||
@@ -71,6 +71,12 @@ func findPluginPaths(kind string, dirs []string) []string { | |||
71 | continue | 71 | continue |
72 | } | 72 | } |
73 | 73 | ||
74 | // Check that the file we found is usable | ||
75 | if !pathIsFile(absPath) { | ||
76 | log.Printf("[ERROR] ignoring non-file %s", absPath) | ||
77 | continue | ||
78 | } | ||
79 | |||
74 | log.Printf("[DEBUG] found %s %q", kind, fullName) | 80 | log.Printf("[DEBUG] found %s %q", kind, fullName) |
75 | ret = append(ret, filepath.Clean(absPath)) | 81 | ret = append(ret, filepath.Clean(absPath)) |
76 | continue | 82 | continue |
@@ -83,7 +89,13 @@ func findPluginPaths(kind string, dirs []string) []string { | |||
83 | continue | 89 | continue |
84 | } | 90 | } |
85 | 91 | ||
86 | log.Printf("[WARNING] found legacy %s %q", kind, fullName) | 92 | // Check that the file we found is usable |
93 | if !pathIsFile(absPath) { | ||
94 | log.Printf("[ERROR] ignoring non-file %s", absPath) | ||
95 | continue | ||
96 | } | ||
97 | |||
98 | log.Printf("[WARN] found legacy %s %q", kind, fullName) | ||
87 | 99 | ||
88 | ret = append(ret, filepath.Clean(absPath)) | 100 | ret = append(ret, filepath.Clean(absPath)) |
89 | } | 101 | } |
@@ -92,6 +104,17 @@ func findPluginPaths(kind string, dirs []string) []string { | |||
92 | return ret | 104 | return ret |
93 | } | 105 | } |
94 | 106 | ||
107 | // Returns true if and only if the given path refers to a file or a symlink | ||
108 | // to a file. | ||
109 | func pathIsFile(path string) bool { | ||
110 | info, err := os.Stat(path) | ||
111 | if err != nil { | ||
112 | return false | ||
113 | } | ||
114 | |||
115 | return !info.IsDir() | ||
116 | } | ||
117 | |||
95 | // ResolvePluginPaths takes a list of paths to plugin executables (as returned | 118 | // ResolvePluginPaths takes a list of paths to plugin executables (as returned |
96 | // by e.g. FindPluginPaths) and produces a PluginMetaSet describing the | 119 | // by e.g. FindPluginPaths) and produces a PluginMetaSet describing the |
97 | // referenced plugins. | 120 | // referenced plugins. |
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go index 241b5cb..815640f 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go +++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/get.go | |||
@@ -3,19 +3,22 @@ package discovery | |||
3 | import ( | 3 | import ( |
4 | "errors" | 4 | "errors" |
5 | "fmt" | 5 | "fmt" |
6 | "io" | ||
6 | "io/ioutil" | 7 | "io/ioutil" |
7 | "log" | 8 | "log" |
8 | "net/http" | 9 | "net/http" |
9 | "os" | 10 | "os" |
11 | "path/filepath" | ||
10 | "runtime" | 12 | "runtime" |
11 | "strconv" | 13 | "strconv" |
12 | "strings" | 14 | "strings" |
13 | 15 | ||
14 | "golang.org/x/net/html" | 16 | "golang.org/x/net/html" |
15 | 17 | ||
16 | cleanhttp "github.com/hashicorp/go-cleanhttp" | ||
17 | getter "github.com/hashicorp/go-getter" | 18 | getter "github.com/hashicorp/go-getter" |
18 | multierror "github.com/hashicorp/go-multierror" | 19 | multierror "github.com/hashicorp/go-multierror" |
20 | "github.com/hashicorp/terraform/httpclient" | ||
21 | "github.com/mitchellh/cli" | ||
19 | ) | 22 | ) |
20 | 23 | ||
21 | // Releases are located by parsing the html listing from releases.hashicorp.com. | 24 | // Releases are located by parsing the html listing from releases.hashicorp.com. |
@@ -30,7 +33,19 @@ const protocolVersionHeader = "x-terraform-protocol-version" | |||
30 | 33 | ||
31 | var releaseHost = "https://releases.hashicorp.com" | 34 | var releaseHost = "https://releases.hashicorp.com" |
32 | 35 | ||
33 | var httpClient = cleanhttp.DefaultClient() | 36 | var httpClient *http.Client |
37 | |||
38 | func init() { | ||
39 | httpClient = httpclient.New() | ||
40 | |||
41 | httpGetter := &getter.HttpGetter{ | ||
42 | Client: httpClient, | ||
43 | Netrc: true, | ||
44 | } | ||
45 | |||
46 | getter.Getters["http"] = httpGetter | ||
47 | getter.Getters["https"] = httpGetter | ||
48 | } | ||
34 | 49 | ||
35 | // An Installer maintains a local cache of plugins by downloading plugins | 50 | // An Installer maintains a local cache of plugins by downloading plugins |
36 | // from an online repository. | 51 | // from an online repository. |
@@ -47,6 +62,10 @@ type Installer interface { | |||
47 | type ProviderInstaller struct { | 62 | type ProviderInstaller struct { |
48 | Dir string | 63 | Dir string |
49 | 64 | ||
65 | // Cache is used to access and update a local cache of plugins if non-nil. | ||
66 | // Can be nil to disable caching. | ||
67 | Cache PluginCache | ||
68 | |||
50 | PluginProtocolVersion uint | 69 | PluginProtocolVersion uint |
51 | 70 | ||
52 | // OS and Arch specify the OS and architecture that should be used when | 71 | // OS and Arch specify the OS and architecture that should be used when |
@@ -58,6 +77,8 @@ type ProviderInstaller struct { | |||
58 | 77 | ||
59 | // Skip checksum and signature verification | 78 | // Skip checksum and signature verification |
60 | SkipVerify bool | 79 | SkipVerify bool |
80 | |||
81 | Ui cli.Ui // Ui for output | ||
61 | } | 82 | } |
62 | 83 | ||
63 | // Get is part of an implementation of type Installer, and attempts to download | 84 | // Get is part of an implementation of type Installer, and attempts to download |
@@ -98,6 +119,12 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e | |||
98 | // sort them newest to oldest | 119 | // sort them newest to oldest |
99 | Versions(versions).Sort() | 120 | Versions(versions).Sort() |
100 | 121 | ||
122 | // Ensure that our installation directory exists | ||
123 | err = os.MkdirAll(i.Dir, os.ModePerm) | ||
124 | if err != nil { | ||
125 | return PluginMeta{}, fmt.Errorf("failed to create plugin dir %s: %s", i.Dir, err) | ||
126 | } | ||
127 | |||
101 | // take the first matching plugin we find | 128 | // take the first matching plugin we find |
102 | for _, v := range versions { | 129 | for _, v := range versions { |
103 | url := i.providerURL(provider, v.String()) | 130 | url := i.providerURL(provider, v.String()) |
@@ -116,8 +143,9 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e | |||
116 | 143 | ||
117 | log.Printf("[DEBUG] fetching provider info for %s version %s", provider, v) | 144 | log.Printf("[DEBUG] fetching provider info for %s version %s", provider, v) |
118 | if checkPlugin(url, i.PluginProtocolVersion) { | 145 | if checkPlugin(url, i.PluginProtocolVersion) { |
119 | log.Printf("[DEBUG] getting provider %q version %q at %s", provider, v, url) | 146 | i.Ui.Info(fmt.Sprintf("- Downloading plugin for provider %q (%s)...", provider, v.String())) |
120 | err := getter.Get(i.Dir, url) | 147 | log.Printf("[DEBUG] getting provider %q version %q", provider, v) |
148 | err := i.install(provider, v, url) | ||
121 | if err != nil { | 149 | if err != nil { |
122 | return PluginMeta{}, err | 150 | return PluginMeta{}, err |
123 | } | 151 | } |
@@ -164,6 +192,98 @@ func (i *ProviderInstaller) Get(provider string, req Constraints) (PluginMeta, e | |||
164 | return PluginMeta{}, ErrorNoVersionCompatible | 192 | return PluginMeta{}, ErrorNoVersionCompatible |
165 | } | 193 | } |
166 | 194 | ||
195 | func (i *ProviderInstaller) install(provider string, version Version, url string) error { | ||
196 | if i.Cache != nil { | ||
197 | log.Printf("[DEBUG] looking for provider %s %s in plugin cache", provider, version) | ||
198 | cached := i.Cache.CachedPluginPath("provider", provider, version) | ||
199 | if cached == "" { | ||
200 | log.Printf("[DEBUG] %s %s not yet in cache, so downloading %s", provider, version, url) | ||
201 | err := getter.Get(i.Cache.InstallDir(), url) | ||
202 | if err != nil { | ||
203 | return err | ||
204 | } | ||
205 | // should now be in cache | ||
206 | cached = i.Cache.CachedPluginPath("provider", provider, version) | ||
207 | if cached == "" { | ||
208 | // should never happen if the getter is behaving properly | ||
209 | // and the plugins are packaged properly. | ||
210 | return fmt.Errorf("failed to find downloaded plugin in cache %s", i.Cache.InstallDir()) | ||
211 | } | ||
212 | } | ||
213 | |||
214 | // Link or copy the cached binary into our install dir so the | ||
215 | // normal resolution machinery can find it. | ||
216 | filename := filepath.Base(cached) | ||
217 | targetPath := filepath.Join(i.Dir, filename) | ||
218 | |||
219 | log.Printf("[DEBUG] installing %s %s to %s from local cache %s", provider, version, targetPath, cached) | ||
220 | |||
221 | // Delete if we can. If there's nothing there already then no harm done. | ||
222 | // This is important because we can't create a link if there's | ||
223 | // already a file of the same name present. | ||
224 | // (any other error here we'll catch below when we try to write here) | ||
225 | os.Remove(targetPath) | ||
226 | |||
227 | // We don't attempt linking on Windows because links are not | ||
228 | // comprehensively supported by all tools/apps in Windows and | ||
229 | // so we choose to be conservative to avoid creating any | ||
230 | // weird issues for Windows users. | ||
231 | linkErr := errors.New("link not supported for Windows") // placeholder error, never actually returned | ||
232 | if runtime.GOOS != "windows" { | ||
233 | // Try hard linking first. Hard links are preferable because this | ||
234 | // creates a self-contained directory that doesn't depend on the | ||
235 | // cache after install. | ||
236 | linkErr = os.Link(cached, targetPath) | ||
237 | |||
238 | // If that failed, try a symlink. This _does_ depend on the cache | ||
239 | // after install, so the user must manage the cache more carefully | ||
240 | // in this case, but avoids creating redundant copies of the | ||
241 | // plugins on disk. | ||
242 | if linkErr != nil { | ||
243 | linkErr = os.Symlink(cached, targetPath) | ||
244 | } | ||
245 | } | ||
246 | |||
247 | // If we still have an error then we'll try a copy as a fallback. | ||
248 | // In this case either the OS is Windows or the target filesystem | ||
249 | // can't support symlinks. | ||
250 | if linkErr != nil { | ||
251 | srcFile, err := os.Open(cached) | ||
252 | if err != nil { | ||
253 | return fmt.Errorf("failed to open cached plugin %s: %s", cached, err) | ||
254 | } | ||
255 | defer srcFile.Close() | ||
256 | |||
257 | destFile, err := os.OpenFile(targetPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, os.ModePerm) | ||
258 | if err != nil { | ||
259 | return fmt.Errorf("failed to create %s: %s", targetPath, err) | ||
260 | } | ||
261 | |||
262 | _, err = io.Copy(destFile, srcFile) | ||
263 | if err != nil { | ||
264 | destFile.Close() | ||
265 | return fmt.Errorf("failed to copy cached plugin from %s to %s: %s", cached, targetPath, err) | ||
266 | } | ||
267 | |||
268 | err = destFile.Close() | ||
269 | if err != nil { | ||
270 | return fmt.Errorf("error creating %s: %s", targetPath, err) | ||
271 | } | ||
272 | } | ||
273 | |||
274 | // One way or another, by the time we get here we should have either | ||
275 | // a link or a copy of the cached plugin within i.Dir, as expected. | ||
276 | } else { | ||
277 | log.Printf("[DEBUG] plugin cache is disabled, so downloading %s %s from %s", provider, version, url) | ||
278 | err := getter.Get(i.Dir, url) | ||
279 | if err != nil { | ||
280 | return err | ||
281 | } | ||
282 | } | ||
283 | |||
284 | return nil | ||
285 | } | ||
286 | |||
167 | func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaSet, error) { | 287 | func (i *ProviderInstaller) PurgeUnused(used map[string]PluginMeta) (PluginMetaSet, error) { |
168 | purge := make(PluginMetaSet) | 288 | purge := make(PluginMetaSet) |
169 | 289 | ||
@@ -261,7 +381,7 @@ func checkPlugin(url string, pluginProtocolVersion uint) bool { | |||
261 | if proto == "" { | 381 | if proto == "" { |
262 | // The header isn't present, but we don't make this error fatal since | 382 | // The header isn't present, but we don't make this error fatal since |
263 | // the latest version will probably work. | 383 | // the latest version will probably work. |
264 | log.Printf("[WARNING] missing %s from: %s", protocolVersionHeader, url) | 384 | log.Printf("[WARN] missing %s from: %s", protocolVersionHeader, url) |
265 | return true | 385 | return true |
266 | } | 386 | } |
267 | 387 | ||
@@ -422,3 +542,7 @@ func getFile(url string) ([]byte, error) { | |||
422 | } | 542 | } |
423 | return data, nil | 543 | return data, nil |
424 | } | 544 | } |
545 | |||
546 | func GetReleaseHost() string { | ||
547 | return releaseHost | ||
548 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go b/vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go new file mode 100644 index 0000000..1a10042 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/plugin/discovery/get_cache.go | |||
@@ -0,0 +1,48 @@ | |||
1 | package discovery | ||
2 | |||
3 | // PluginCache is an interface implemented by objects that are able to maintain | ||
4 | // a cache of plugins. | ||
5 | type PluginCache interface { | ||
6 | // CachedPluginPath returns a path where the requested plugin is already | ||
7 | // cached, or an empty string if the requested plugin is not yet cached. | ||
8 | CachedPluginPath(kind string, name string, version Version) string | ||
9 | |||
10 | // InstallDir returns the directory that new plugins should be installed into | ||
11 | // in order to populate the cache. This directory should be used as the | ||
12 | // first argument to getter.Get when downloading plugins with go-getter. | ||
13 | // | ||
14 | // After installing into this directory, use CachedPluginPath to obtain the | ||
15 | // path where the plugin was installed. | ||
16 | InstallDir() string | ||
17 | } | ||
18 | |||
19 | // NewLocalPluginCache returns a PluginCache that caches plugins in a | ||
20 | // given local directory. | ||
21 | func NewLocalPluginCache(dir string) PluginCache { | ||
22 | return &pluginCache{ | ||
23 | Dir: dir, | ||
24 | } | ||
25 | } | ||
26 | |||
27 | type pluginCache struct { | ||
28 | Dir string | ||
29 | } | ||
30 | |||
31 | func (c *pluginCache) CachedPluginPath(kind string, name string, version Version) string { | ||
32 | allPlugins := FindPlugins(kind, []string{c.Dir}) | ||
33 | plugins := allPlugins.WithName(name).WithVersion(version) | ||
34 | |||
35 | if plugins.Count() == 0 { | ||
36 | // nothing cached | ||
37 | return "" | ||
38 | } | ||
39 | |||
40 | // There should generally be only one plugin here; if there's more than | ||
41 | // one match for some reason then we'll just choose one arbitrarily. | ||
42 | plugin := plugins.Newest() | ||
43 | return plugin.Path | ||
44 | } | ||
45 | |||
46 | func (c *pluginCache) InstallDir() string { | ||
47 | return c.Dir | ||
48 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go b/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go index 473f786..d6a433c 100644 --- a/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go +++ b/vendor/github.com/hashicorp/terraform/plugin/resource_provider.go | |||
@@ -41,6 +41,24 @@ func (p *ResourceProvider) Stop() error { | |||
41 | return err | 41 | return err |
42 | } | 42 | } |
43 | 43 | ||
44 | func (p *ResourceProvider) GetSchema(req *terraform.ProviderSchemaRequest) (*terraform.ProviderSchema, error) { | ||
45 | var result ResourceProviderGetSchemaResponse | ||
46 | args := &ResourceProviderGetSchemaArgs{ | ||
47 | Req: req, | ||
48 | } | ||
49 | |||
50 | err := p.Client.Call("Plugin.GetSchema", args, &result) | ||
51 | if err != nil { | ||
52 | return nil, err | ||
53 | } | ||
54 | |||
55 | if result.Error != nil { | ||
56 | err = result.Error | ||
57 | } | ||
58 | |||
59 | return result.Schema, err | ||
60 | } | ||
61 | |||
44 | func (p *ResourceProvider) Input( | 62 | func (p *ResourceProvider) Input( |
45 | input terraform.UIInput, | 63 | input terraform.UIInput, |
46 | c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { | 64 | c *terraform.ResourceConfig) (*terraform.ResourceConfig, error) { |
@@ -312,6 +330,15 @@ type ResourceProviderStopResponse struct { | |||
312 | Error *plugin.BasicError | 330 | Error *plugin.BasicError |
313 | } | 331 | } |
314 | 332 | ||
333 | type ResourceProviderGetSchemaArgs struct { | ||
334 | Req *terraform.ProviderSchemaRequest | ||
335 | } | ||
336 | |||
337 | type ResourceProviderGetSchemaResponse struct { | ||
338 | Schema *terraform.ProviderSchema | ||
339 | Error *plugin.BasicError | ||
340 | } | ||
341 | |||
315 | type ResourceProviderConfigureResponse struct { | 342 | type ResourceProviderConfigureResponse struct { |
316 | Error *plugin.BasicError | 343 | Error *plugin.BasicError |
317 | } | 344 | } |
@@ -418,6 +445,18 @@ func (s *ResourceProviderServer) Stop( | |||
418 | return nil | 445 | return nil |
419 | } | 446 | } |
420 | 447 | ||
448 | func (s *ResourceProviderServer) GetSchema( | ||
449 | args *ResourceProviderGetSchemaArgs, | ||
450 | result *ResourceProviderGetSchemaResponse, | ||
451 | ) error { | ||
452 | schema, err := s.Provider.GetSchema(args.Req) | ||
453 | result.Schema = schema | ||
454 | if err != nil { | ||
455 | result.Error = plugin.NewBasicError(err) | ||
456 | } | ||
457 | return nil | ||
458 | } | ||
459 | |||
421 | func (s *ResourceProviderServer) Input( | 460 | func (s *ResourceProviderServer) Input( |
422 | args *ResourceProviderInputArgs, | 461 | args *ResourceProviderInputArgs, |
423 | reply *ResourceProviderInputResponse) error { | 462 | reply *ResourceProviderInputResponse) error { |
diff --git a/vendor/github.com/hashicorp/terraform/registry/client.go b/vendor/github.com/hashicorp/terraform/registry/client.go new file mode 100644 index 0000000..a18e6b8 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/client.go | |||
@@ -0,0 +1,227 @@ | |||
1 | package registry | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | "io/ioutil" | ||
7 | "log" | ||
8 | "net/http" | ||
9 | "net/url" | ||
10 | "path" | ||
11 | "strings" | ||
12 | "time" | ||
13 | |||
14 | "github.com/hashicorp/terraform/httpclient" | ||
15 | "github.com/hashicorp/terraform/registry/regsrc" | ||
16 | "github.com/hashicorp/terraform/registry/response" | ||
17 | "github.com/hashicorp/terraform/svchost" | ||
18 | "github.com/hashicorp/terraform/svchost/disco" | ||
19 | "github.com/hashicorp/terraform/version" | ||
20 | ) | ||
21 | |||
22 | const ( | ||
23 | xTerraformGet = "X-Terraform-Get" | ||
24 | xTerraformVersion = "X-Terraform-Version" | ||
25 | requestTimeout = 10 * time.Second | ||
26 | serviceID = "modules.v1" | ||
27 | ) | ||
28 | |||
29 | var tfVersion = version.String() | ||
30 | |||
31 | // Client provides methods to query Terraform Registries. | ||
32 | type Client struct { | ||
33 | // this is the client to be used for all requests. | ||
34 | client *http.Client | ||
35 | |||
36 | // services is a required *disco.Disco, which may have services and | ||
37 | // credentials pre-loaded. | ||
38 | services *disco.Disco | ||
39 | } | ||
40 | |||
41 | // NewClient returns a new initialized registry client. | ||
42 | func NewClient(services *disco.Disco, client *http.Client) *Client { | ||
43 | if services == nil { | ||
44 | services = disco.New() | ||
45 | } | ||
46 | |||
47 | if client == nil { | ||
48 | client = httpclient.New() | ||
49 | client.Timeout = requestTimeout | ||
50 | } | ||
51 | |||
52 | services.Transport = client.Transport | ||
53 | |||
54 | return &Client{ | ||
55 | client: client, | ||
56 | services: services, | ||
57 | } | ||
58 | } | ||
59 | |||
60 | // Discover queries the host, and returns the url for the registry. | ||
61 | func (c *Client) Discover(host svchost.Hostname) (*url.URL, error) { | ||
62 | service, err := c.services.DiscoverServiceURL(host, serviceID) | ||
63 | if err != nil { | ||
64 | return nil, err | ||
65 | } | ||
66 | if !strings.HasSuffix(service.Path, "/") { | ||
67 | service.Path += "/" | ||
68 | } | ||
69 | return service, nil | ||
70 | } | ||
71 | |||
72 | // Versions queries the registry for a module, and returns the available versions. | ||
73 | func (c *Client) Versions(module *regsrc.Module) (*response.ModuleVersions, error) { | ||
74 | host, err := module.SvcHost() | ||
75 | if err != nil { | ||
76 | return nil, err | ||
77 | } | ||
78 | |||
79 | service, err := c.Discover(host) | ||
80 | if err != nil { | ||
81 | return nil, err | ||
82 | } | ||
83 | |||
84 | p, err := url.Parse(path.Join(module.Module(), "versions")) | ||
85 | if err != nil { | ||
86 | return nil, err | ||
87 | } | ||
88 | |||
89 | service = service.ResolveReference(p) | ||
90 | |||
91 | log.Printf("[DEBUG] fetching module versions from %q", service) | ||
92 | |||
93 | req, err := http.NewRequest("GET", service.String(), nil) | ||
94 | if err != nil { | ||
95 | return nil, err | ||
96 | } | ||
97 | |||
98 | c.addRequestCreds(host, req) | ||
99 | req.Header.Set(xTerraformVersion, tfVersion) | ||
100 | |||
101 | resp, err := c.client.Do(req) | ||
102 | if err != nil { | ||
103 | return nil, err | ||
104 | } | ||
105 | defer resp.Body.Close() | ||
106 | |||
107 | switch resp.StatusCode { | ||
108 | case http.StatusOK: | ||
109 | // OK | ||
110 | case http.StatusNotFound: | ||
111 | return nil, &errModuleNotFound{addr: module} | ||
112 | default: | ||
113 | return nil, fmt.Errorf("error looking up module versions: %s", resp.Status) | ||
114 | } | ||
115 | |||
116 | var versions response.ModuleVersions | ||
117 | |||
118 | dec := json.NewDecoder(resp.Body) | ||
119 | if err := dec.Decode(&versions); err != nil { | ||
120 | return nil, err | ||
121 | } | ||
122 | |||
123 | for _, mod := range versions.Modules { | ||
124 | for _, v := range mod.Versions { | ||
125 | log.Printf("[DEBUG] found available version %q for %s", v.Version, mod.Source) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | return &versions, nil | ||
130 | } | ||
131 | |||
132 | func (c *Client) addRequestCreds(host svchost.Hostname, req *http.Request) { | ||
133 | creds, err := c.services.CredentialsForHost(host) | ||
134 | if err != nil { | ||
135 | log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", host, err) | ||
136 | return | ||
137 | } | ||
138 | |||
139 | if creds != nil { | ||
140 | creds.PrepareRequest(req) | ||
141 | } | ||
142 | } | ||
143 | |||
144 | // Location find the download location for a specific version module. | ||
145 | // This returns a string, because the final location may contain special go-getter syntax. | ||
146 | func (c *Client) Location(module *regsrc.Module, version string) (string, error) { | ||
147 | host, err := module.SvcHost() | ||
148 | if err != nil { | ||
149 | return "", err | ||
150 | } | ||
151 | |||
152 | service, err := c.Discover(host) | ||
153 | if err != nil { | ||
154 | return "", err | ||
155 | } | ||
156 | |||
157 | var p *url.URL | ||
158 | if version == "" { | ||
159 | p, err = url.Parse(path.Join(module.Module(), "download")) | ||
160 | } else { | ||
161 | p, err = url.Parse(path.Join(module.Module(), version, "download")) | ||
162 | } | ||
163 | if err != nil { | ||
164 | return "", err | ||
165 | } | ||
166 | download := service.ResolveReference(p) | ||
167 | |||
168 | log.Printf("[DEBUG] looking up module location from %q", download) | ||
169 | |||
170 | req, err := http.NewRequest("GET", download.String(), nil) | ||
171 | if err != nil { | ||
172 | return "", err | ||
173 | } | ||
174 | |||
175 | c.addRequestCreds(host, req) | ||
176 | req.Header.Set(xTerraformVersion, tfVersion) | ||
177 | |||
178 | resp, err := c.client.Do(req) | ||
179 | if err != nil { | ||
180 | return "", err | ||
181 | } | ||
182 | defer resp.Body.Close() | ||
183 | |||
184 | // there should be no body, but save it for logging | ||
185 | body, err := ioutil.ReadAll(resp.Body) | ||
186 | if err != nil { | ||
187 | return "", fmt.Errorf("error reading response body from registry: %s", err) | ||
188 | } | ||
189 | |||
190 | switch resp.StatusCode { | ||
191 | case http.StatusOK, http.StatusNoContent: | ||
192 | // OK | ||
193 | case http.StatusNotFound: | ||
194 | return "", fmt.Errorf("module %q version %q not found", module, version) | ||
195 | default: | ||
196 | // anything else is an error: | ||
197 | return "", fmt.Errorf("error getting download location for %q: %s resp:%s", module, resp.Status, body) | ||
198 | } | ||
199 | |||
200 | // the download location is in the X-Terraform-Get header | ||
201 | location := resp.Header.Get(xTerraformGet) | ||
202 | if location == "" { | ||
203 | return "", fmt.Errorf("failed to get download URL for %q: %s resp:%s", module, resp.Status, body) | ||
204 | } | ||
205 | |||
206 | // If location looks like it's trying to be a relative URL, treat it as | ||
207 | // one. | ||
208 | // | ||
209 | // We don't do this for just _any_ location, since the X-Terraform-Get | ||
210 | // header is a go-getter location rather than a URL, and so not all | ||
211 | // possible values will parse reasonably as URLs.) | ||
212 | // | ||
213 | // When used in conjunction with go-getter we normally require this header | ||
214 | // to be an absolute URL, but we are more liberal here because third-party | ||
215 | // registry implementations may not "know" their own absolute URLs if | ||
216 | // e.g. they are running behind a reverse proxy frontend, or such. | ||
217 | if strings.HasPrefix(location, "/") || strings.HasPrefix(location, "./") || strings.HasPrefix(location, "../") { | ||
218 | locationURL, err := url.Parse(location) | ||
219 | if err != nil { | ||
220 | return "", fmt.Errorf("invalid relative URL for %q: %s", module, err) | ||
221 | } | ||
222 | locationURL = download.ResolveReference(locationURL) | ||
223 | location = locationURL.String() | ||
224 | } | ||
225 | |||
226 | return location, nil | ||
227 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/errors.go b/vendor/github.com/hashicorp/terraform/registry/errors.go new file mode 100644 index 0000000..b8dcd31 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/errors.go | |||
@@ -0,0 +1,23 @@ | |||
1 | package registry | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/terraform/registry/regsrc" | ||
7 | ) | ||
8 | |||
9 | type errModuleNotFound struct { | ||
10 | addr *regsrc.Module | ||
11 | } | ||
12 | |||
13 | func (e *errModuleNotFound) Error() string { | ||
14 | return fmt.Sprintf("module %s not found", e.addr) | ||
15 | } | ||
16 | |||
17 | // IsModuleNotFound returns true only if the given error is a "module not found" | ||
18 | // error. This allows callers to recognize this particular error condition | ||
19 | // as distinct from operational errors such as poor network connectivity. | ||
20 | func IsModuleNotFound(err error) bool { | ||
21 | _, ok := err.(*errModuleNotFound) | ||
22 | return ok | ||
23 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/regsrc/friendly_host.go b/vendor/github.com/hashicorp/terraform/registry/regsrc/friendly_host.go new file mode 100644 index 0000000..14b4dce --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/regsrc/friendly_host.go | |||
@@ -0,0 +1,140 @@ | |||
1 | package regsrc | ||
2 | |||
3 | import ( | ||
4 | "regexp" | ||
5 | "strings" | ||
6 | |||
7 | "github.com/hashicorp/terraform/svchost" | ||
8 | ) | ||
9 | |||
10 | var ( | ||
11 | // InvalidHostString is a placeholder returned when a raw host can't be | ||
12 | // converted by IDNA spec. It will never be returned for any host for which | ||
13 | // Valid() is true. | ||
14 | InvalidHostString = "<invalid host>" | ||
15 | |||
16 | // urlLabelEndSubRe is a sub-expression that matches any character that's | ||
17 | // allowed at the start or end of a URL label according to RFC1123. | ||
18 | urlLabelEndSubRe = "[0-9A-Za-z]" | ||
19 | |||
20 | // urlLabelEndSubRe is a sub-expression that matches any character that's | ||
21 | // allowed at in a non-start or end of a URL label according to RFC1123. | ||
22 | urlLabelMidSubRe = "[0-9A-Za-z-]" | ||
23 | |||
24 | // urlLabelUnicodeSubRe is a sub-expression that matches any non-ascii char | ||
25 | // in an IDN (Unicode) display URL. It's not strict - there are only ~15k | ||
26 | // valid Unicode points in IDN RFC (some with conditions). We are just going | ||
27 | // with being liberal with matching and then erroring if we fail to convert | ||
28 | // to punycode later (which validates chars fully). This at least ensures | ||
29 | // ascii chars dissalowed by the RC1123 parts above don't become legal | ||
30 | // again. | ||
31 | urlLabelUnicodeSubRe = "[^[:ascii:]]" | ||
32 | |||
33 | // hostLabelSubRe is the sub-expression that matches a valid hostname label. | ||
34 | // It does not anchor the start or end so it can be composed into more | ||
35 | // complex RegExps below. Note that for sanity we don't handle disallowing | ||
36 | // raw punycode in this regexp (esp. since re2 doesn't support negative | ||
37 | // lookbehind, but we can capture it's presence here to check later). | ||
38 | hostLabelSubRe = "" + | ||
39 | // Match valid initial char, or unicode char | ||
40 | "(?:" + urlLabelEndSubRe + "|" + urlLabelUnicodeSubRe + ")" + | ||
41 | // Optionally, match 0 to 61 valid URL or Unicode chars, | ||
42 | // followed by one valid end char or unicode char | ||
43 | "(?:" + | ||
44 | "(?:" + urlLabelMidSubRe + "|" + urlLabelUnicodeSubRe + "){0,61}" + | ||
45 | "(?:" + urlLabelEndSubRe + "|" + urlLabelUnicodeSubRe + ")" + | ||
46 | ")?" | ||
47 | |||
48 | // hostSubRe is the sub-expression that matches a valid host prefix. | ||
49 | // Allows custom port. | ||
50 | hostSubRe = hostLabelSubRe + "(?:\\." + hostLabelSubRe + ")+(?::\\d+)?" | ||
51 | |||
52 | // hostRe is a regexp that matches a valid host prefix. Additional | ||
53 | // validation of unicode strings is needed for matches. | ||
54 | hostRe = regexp.MustCompile("^" + hostSubRe + "$") | ||
55 | ) | ||
56 | |||
57 | // FriendlyHost describes a registry instance identified in source strings by a | ||
58 | // simple bare hostname like registry.terraform.io. | ||
59 | type FriendlyHost struct { | ||
60 | Raw string | ||
61 | } | ||
62 | |||
63 | func NewFriendlyHost(host string) *FriendlyHost { | ||
64 | return &FriendlyHost{Raw: host} | ||
65 | } | ||
66 | |||
67 | // ParseFriendlyHost attempts to parse a valid "friendly host" prefix from the | ||
68 | // given string. If no valid prefix is found, host will be nil and rest will | ||
69 | // contain the full source string. The host prefix must terminate at the end of | ||
70 | // the input or at the first / character. If one or more characters exist after | ||
71 | // the first /, they will be returned as rest (without the / delimiter). | ||
72 | // Hostnames containing punycode WILL be parsed successfully since they may have | ||
73 | // come from an internal normalized source string, however should be considered | ||
74 | // invalid if the string came from a user directly. This must be checked | ||
75 | // explicitly for user-input strings by calling Valid() on the | ||
76 | // returned host. | ||
77 | func ParseFriendlyHost(source string) (host *FriendlyHost, rest string) { | ||
78 | parts := strings.SplitN(source, "/", 2) | ||
79 | |||
80 | if hostRe.MatchString(parts[0]) { | ||
81 | host = &FriendlyHost{Raw: parts[0]} | ||
82 | if len(parts) == 2 { | ||
83 | rest = parts[1] | ||
84 | } | ||
85 | return | ||
86 | } | ||
87 | |||
88 | // No match, return whole string as rest along with nil host | ||
89 | rest = source | ||
90 | return | ||
91 | } | ||
92 | |||
93 | // Valid returns whether the host prefix is considered valid in any case. | ||
94 | // Example of invalid prefixes might include ones that don't conform to the host | ||
95 | // name specifications. Not that IDN prefixes containing punycode are not valid | ||
96 | // input which we expect to always be in user-input or normalised display form. | ||
97 | func (h *FriendlyHost) Valid() bool { | ||
98 | return svchost.IsValid(h.Raw) | ||
99 | } | ||
100 | |||
101 | // Display returns the host formatted for display to the user in CLI or web | ||
102 | // output. | ||
103 | func (h *FriendlyHost) Display() string { | ||
104 | return svchost.ForDisplay(h.Raw) | ||
105 | } | ||
106 | |||
107 | // Normalized returns the host formatted for internal reference or comparison. | ||
108 | func (h *FriendlyHost) Normalized() string { | ||
109 | host, err := svchost.ForComparison(h.Raw) | ||
110 | if err != nil { | ||
111 | return InvalidHostString | ||
112 | } | ||
113 | return string(host) | ||
114 | } | ||
115 | |||
116 | // String returns the host formatted as the user originally typed it assuming it | ||
117 | // was parsed from user input. | ||
118 | func (h *FriendlyHost) String() string { | ||
119 | return h.Raw | ||
120 | } | ||
121 | |||
122 | // Equal compares the FriendlyHost against another instance taking normalization | ||
123 | // into account. Invalid hosts cannot be compared and will always return false. | ||
124 | func (h *FriendlyHost) Equal(other *FriendlyHost) bool { | ||
125 | if other == nil { | ||
126 | return false | ||
127 | } | ||
128 | |||
129 | otherHost, err := svchost.ForComparison(other.Raw) | ||
130 | if err != nil { | ||
131 | return false | ||
132 | } | ||
133 | |||
134 | host, err := svchost.ForComparison(h.Raw) | ||
135 | if err != nil { | ||
136 | return false | ||
137 | } | ||
138 | |||
139 | return otherHost == host | ||
140 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go b/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go new file mode 100644 index 0000000..325706e --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/regsrc/module.go | |||
@@ -0,0 +1,205 @@ | |||
1 | package regsrc | ||
2 | |||
3 | import ( | ||
4 | "errors" | ||
5 | "fmt" | ||
6 | "regexp" | ||
7 | "strings" | ||
8 | |||
9 | "github.com/hashicorp/terraform/svchost" | ||
10 | ) | ||
11 | |||
12 | var ( | ||
13 | ErrInvalidModuleSource = errors.New("not a valid registry module source") | ||
14 | |||
15 | // nameSubRe is the sub-expression that matches a valid module namespace or | ||
16 | // name. It's strictly a super-set of what GitHub allows for user/org and | ||
17 | // repo names respectively, but more restrictive than our original repo-name | ||
18 | // regex which allowed periods but could cause ambiguity with hostname | ||
19 | // prefixes. It does not anchor the start or end so it can be composed into | ||
20 | // more complex RegExps below. Alphanumeric with - and _ allowed in non | ||
21 | // leading or trailing positions. Max length 64 chars. (GitHub username is | ||
22 | // 38 max.) | ||
23 | nameSubRe = "[0-9A-Za-z](?:[0-9A-Za-z-_]{0,62}[0-9A-Za-z])?" | ||
24 | |||
25 | // providerSubRe is the sub-expression that matches a valid provider. It | ||
26 | // does not anchor the start or end so it can be composed into more complex | ||
27 | // RegExps below. Only lowercase chars and digits are supported in practice. | ||
28 | // Max length 64 chars. | ||
29 | providerSubRe = "[0-9a-z]{1,64}" | ||
30 | |||
31 | // moduleSourceRe is a regular expression that matches the basic | ||
32 | // namespace/name/provider[//...] format for registry sources. It assumes | ||
33 | // any FriendlyHost prefix has already been removed if present. | ||
34 | moduleSourceRe = regexp.MustCompile( | ||
35 | fmt.Sprintf("^(%s)\\/(%s)\\/(%s)(?:\\/\\/(.*))?$", | ||
36 | nameSubRe, nameSubRe, providerSubRe)) | ||
37 | |||
38 | // NameRe is a regular expression defining the format allowed for namespace | ||
39 | // or name fields in module registry implementations. | ||
40 | NameRe = regexp.MustCompile("^" + nameSubRe + "$") | ||
41 | |||
42 | // ProviderRe is a regular expression defining the format allowed for | ||
43 | // provider fields in module registry implementations. | ||
44 | ProviderRe = regexp.MustCompile("^" + providerSubRe + "$") | ||
45 | |||
46 | // these hostnames are not allowed as registry sources, because they are | ||
47 | // already special case module sources in terraform. | ||
48 | disallowed = map[string]bool{ | ||
49 | "github.com": true, | ||
50 | "bitbucket.org": true, | ||
51 | } | ||
52 | ) | ||
53 | |||
54 | // Module describes a Terraform Registry Module source. | ||
55 | type Module struct { | ||
56 | // RawHost is the friendly host prefix if one was present. It might be nil | ||
57 | // if the original source had no host prefix which implies | ||
58 | // PublicRegistryHost but is distinct from having an actual pointer to | ||
59 | // PublicRegistryHost since it encodes the fact the original string didn't | ||
60 | // include a host prefix at all which is significant for recovering actual | ||
61 | // input not just normalized form. Most callers should access it with Host() | ||
62 | // which will return public registry host instance if it's nil. | ||
63 | RawHost *FriendlyHost | ||
64 | RawNamespace string | ||
65 | RawName string | ||
66 | RawProvider string | ||
67 | RawSubmodule string | ||
68 | } | ||
69 | |||
70 | // NewModule construct a new module source from separate parts. Pass empty | ||
71 | // string if host or submodule are not needed. | ||
72 | func NewModule(host, namespace, name, provider, submodule string) (*Module, error) { | ||
73 | m := &Module{ | ||
74 | RawNamespace: namespace, | ||
75 | RawName: name, | ||
76 | RawProvider: provider, | ||
77 | RawSubmodule: submodule, | ||
78 | } | ||
79 | if host != "" { | ||
80 | h := NewFriendlyHost(host) | ||
81 | if h != nil { | ||
82 | fmt.Println("HOST:", h) | ||
83 | if !h.Valid() || disallowed[h.Display()] { | ||
84 | return nil, ErrInvalidModuleSource | ||
85 | } | ||
86 | } | ||
87 | m.RawHost = h | ||
88 | } | ||
89 | return m, nil | ||
90 | } | ||
91 | |||
92 | // ParseModuleSource attempts to parse source as a Terraform registry module | ||
93 | // source. If the string is not found to be in a valid format, | ||
94 | // ErrInvalidModuleSource is returned. Note that this can only be used on | ||
95 | // "input" strings, e.g. either ones supplied by the user or potentially | ||
96 | // normalised but in Display form (unicode). It will fail to parse a source with | ||
97 | // a punycoded domain since this is not permitted input from a user. If you have | ||
98 | // an already normalized string internally, you can compare it without parsing | ||
99 | // by comparing with the normalized version of the subject with the normal | ||
100 | // string equality operator. | ||
101 | func ParseModuleSource(source string) (*Module, error) { | ||
102 | // See if there is a friendly host prefix. | ||
103 | host, rest := ParseFriendlyHost(source) | ||
104 | if host != nil { | ||
105 | if !host.Valid() || disallowed[host.Display()] { | ||
106 | return nil, ErrInvalidModuleSource | ||
107 | } | ||
108 | } | ||
109 | |||
110 | matches := moduleSourceRe.FindStringSubmatch(rest) | ||
111 | if len(matches) < 4 { | ||
112 | return nil, ErrInvalidModuleSource | ||
113 | } | ||
114 | |||
115 | m := &Module{ | ||
116 | RawHost: host, | ||
117 | RawNamespace: matches[1], | ||
118 | RawName: matches[2], | ||
119 | RawProvider: matches[3], | ||
120 | } | ||
121 | |||
122 | if len(matches) == 5 { | ||
123 | m.RawSubmodule = matches[4] | ||
124 | } | ||
125 | |||
126 | return m, nil | ||
127 | } | ||
128 | |||
129 | // Display returns the source formatted for display to the user in CLI or web | ||
130 | // output. | ||
131 | func (m *Module) Display() string { | ||
132 | return m.formatWithPrefix(m.normalizedHostPrefix(m.Host().Display()), false) | ||
133 | } | ||
134 | |||
135 | // Normalized returns the source formatted for internal reference or comparison. | ||
136 | func (m *Module) Normalized() string { | ||
137 | return m.formatWithPrefix(m.normalizedHostPrefix(m.Host().Normalized()), false) | ||
138 | } | ||
139 | |||
140 | // String returns the source formatted as the user originally typed it assuming | ||
141 | // it was parsed from user input. | ||
142 | func (m *Module) String() string { | ||
143 | // Don't normalize public registry hostname - leave it exactly like the user | ||
144 | // input it. | ||
145 | hostPrefix := "" | ||
146 | if m.RawHost != nil { | ||
147 | hostPrefix = m.RawHost.String() + "/" | ||
148 | } | ||
149 | return m.formatWithPrefix(hostPrefix, true) | ||
150 | } | ||
151 | |||
152 | // Equal compares the module source against another instance taking | ||
153 | // normalization into account. | ||
154 | func (m *Module) Equal(other *Module) bool { | ||
155 | return m.Normalized() == other.Normalized() | ||
156 | } | ||
157 | |||
158 | // Host returns the FriendlyHost object describing which registry this module is | ||
159 | // in. If the original source string had not host component this will return the | ||
160 | // PublicRegistryHost. | ||
161 | func (m *Module) Host() *FriendlyHost { | ||
162 | if m.RawHost == nil { | ||
163 | return PublicRegistryHost | ||
164 | } | ||
165 | return m.RawHost | ||
166 | } | ||
167 | |||
168 | func (m *Module) normalizedHostPrefix(host string) string { | ||
169 | if m.Host().Equal(PublicRegistryHost) { | ||
170 | return "" | ||
171 | } | ||
172 | return host + "/" | ||
173 | } | ||
174 | |||
175 | func (m *Module) formatWithPrefix(hostPrefix string, preserveCase bool) string { | ||
176 | suffix := "" | ||
177 | if m.RawSubmodule != "" { | ||
178 | suffix = "//" + m.RawSubmodule | ||
179 | } | ||
180 | str := fmt.Sprintf("%s%s/%s/%s%s", hostPrefix, m.RawNamespace, m.RawName, | ||
181 | m.RawProvider, suffix) | ||
182 | |||
183 | // lower case by default | ||
184 | if !preserveCase { | ||
185 | return strings.ToLower(str) | ||
186 | } | ||
187 | return str | ||
188 | } | ||
189 | |||
190 | // Module returns just the registry ID of the module, without a hostname or | ||
191 | // suffix. | ||
192 | func (m *Module) Module() string { | ||
193 | return fmt.Sprintf("%s/%s/%s", m.RawNamespace, m.RawName, m.RawProvider) | ||
194 | } | ||
195 | |||
196 | // SvcHost returns the svchost.Hostname for this module. Since FriendlyHost may | ||
197 | // contain an invalid hostname, this also returns an error indicating if it | ||
198 | // could be converted to a svchost.Hostname. If no host is specified, the | ||
199 | // default PublicRegistryHost is returned. | ||
200 | func (m *Module) SvcHost() (svchost.Hostname, error) { | ||
201 | if m.RawHost == nil { | ||
202 | return svchost.ForComparison(PublicRegistryHost.Raw) | ||
203 | } | ||
204 | return svchost.ForComparison(m.RawHost.Raw) | ||
205 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/regsrc/regsrc.go b/vendor/github.com/hashicorp/terraform/registry/regsrc/regsrc.go new file mode 100644 index 0000000..c430bf1 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/regsrc/regsrc.go | |||
@@ -0,0 +1,8 @@ | |||
1 | // Package regsrc provides helpers for working with source strings that identify | ||
2 | // resources within a Terraform registry. | ||
3 | package regsrc | ||
4 | |||
5 | var ( | ||
6 | // PublicRegistryHost is a FriendlyHost that represents the public registry. | ||
7 | PublicRegistryHost = NewFriendlyHost("registry.terraform.io") | ||
8 | ) | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/module.go b/vendor/github.com/hashicorp/terraform/registry/response/module.go new file mode 100644 index 0000000..3bd2b3d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/response/module.go | |||
@@ -0,0 +1,93 @@ | |||
1 | package response | ||
2 | |||
3 | import ( | ||
4 | "time" | ||
5 | ) | ||
6 | |||
7 | // Module is the response structure with the data for a single module version. | ||
8 | type Module struct { | ||
9 | ID string `json:"id"` | ||
10 | |||
11 | //--------------------------------------------------------------- | ||
12 | // Metadata about the overall module. | ||
13 | |||
14 | Owner string `json:"owner"` | ||
15 | Namespace string `json:"namespace"` | ||
16 | Name string `json:"name"` | ||
17 | Version string `json:"version"` | ||
18 | Provider string `json:"provider"` | ||
19 | Description string `json:"description"` | ||
20 | Source string `json:"source"` | ||
21 | PublishedAt time.Time `json:"published_at"` | ||
22 | Downloads int `json:"downloads"` | ||
23 | Verified bool `json:"verified"` | ||
24 | } | ||
25 | |||
26 | // ModuleDetail represents a module in full detail. | ||
27 | type ModuleDetail struct { | ||
28 | Module | ||
29 | |||
30 | //--------------------------------------------------------------- | ||
31 | // Metadata about the overall module. This is only available when | ||
32 | // requesting the specific module (not in list responses). | ||
33 | |||
34 | // Root is the root module. | ||
35 | Root *ModuleSubmodule `json:"root"` | ||
36 | |||
37 | // Submodules are the other submodules that are available within | ||
38 | // this module. | ||
39 | Submodules []*ModuleSubmodule `json:"submodules"` | ||
40 | |||
41 | //--------------------------------------------------------------- | ||
42 | // The fields below are only set when requesting this specific | ||
43 | // module. They are available to easily know all available versions | ||
44 | // and providers without multiple API calls. | ||
45 | |||
46 | Providers []string `json:"providers"` // All available providers | ||
47 | Versions []string `json:"versions"` // All versions | ||
48 | } | ||
49 | |||
50 | // ModuleSubmodule is the metadata about a specific submodule within | ||
51 | // a module. This includes the root module as a special case. | ||
52 | type ModuleSubmodule struct { | ||
53 | Path string `json:"path"` | ||
54 | Readme string `json:"readme"` | ||
55 | Empty bool `json:"empty"` | ||
56 | |||
57 | Inputs []*ModuleInput `json:"inputs"` | ||
58 | Outputs []*ModuleOutput `json:"outputs"` | ||
59 | Dependencies []*ModuleDep `json:"dependencies"` | ||
60 | Resources []*ModuleResource `json:"resources"` | ||
61 | } | ||
62 | |||
63 | // ModuleInput is an input for a module. | ||
64 | type ModuleInput struct { | ||
65 | Name string `json:"name"` | ||
66 | Description string `json:"description"` | ||
67 | Default string `json:"default"` | ||
68 | } | ||
69 | |||
70 | // ModuleOutput is an output for a module. | ||
71 | type ModuleOutput struct { | ||
72 | Name string `json:"name"` | ||
73 | Description string `json:"description"` | ||
74 | } | ||
75 | |||
76 | // ModuleDep is an output for a module. | ||
77 | type ModuleDep struct { | ||
78 | Name string `json:"name"` | ||
79 | Source string `json:"source"` | ||
80 | Version string `json:"version"` | ||
81 | } | ||
82 | |||
83 | // ModuleProviderDep is the output for a provider dependency | ||
84 | type ModuleProviderDep struct { | ||
85 | Name string `json:"name"` | ||
86 | Version string `json:"version"` | ||
87 | } | ||
88 | |||
89 | // ModuleResource is an output for a module. | ||
90 | type ModuleResource struct { | ||
91 | Name string `json:"name"` | ||
92 | Type string `json:"type"` | ||
93 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/module_list.go b/vendor/github.com/hashicorp/terraform/registry/response/module_list.go new file mode 100644 index 0000000..9783748 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/response/module_list.go | |||
@@ -0,0 +1,7 @@ | |||
1 | package response | ||
2 | |||
3 | // ModuleList is the response structure for a pageable list of modules. | ||
4 | type ModuleList struct { | ||
5 | Meta PaginationMeta `json:"meta"` | ||
6 | Modules []*Module `json:"modules"` | ||
7 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/module_provider.go b/vendor/github.com/hashicorp/terraform/registry/response/module_provider.go new file mode 100644 index 0000000..e48499d --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/response/module_provider.go | |||
@@ -0,0 +1,14 @@ | |||
1 | package response | ||
2 | |||
3 | // ModuleProvider represents a single provider for modules. | ||
4 | type ModuleProvider struct { | ||
5 | Name string `json:"name"` | ||
6 | Downloads int `json:"downloads"` | ||
7 | ModuleCount int `json:"module_count"` | ||
8 | } | ||
9 | |||
10 | // ModuleProviderList is the response structure for a pageable list of ModuleProviders. | ||
11 | type ModuleProviderList struct { | ||
12 | Meta PaginationMeta `json:"meta"` | ||
13 | Providers []*ModuleProvider `json:"providers"` | ||
14 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/module_versions.go b/vendor/github.com/hashicorp/terraform/registry/response/module_versions.go new file mode 100644 index 0000000..f69e975 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/response/module_versions.go | |||
@@ -0,0 +1,32 @@ | |||
1 | package response | ||
2 | |||
3 | // ModuleVersions is the response format that contains all metadata about module | ||
4 | // versions needed for terraform CLI to resolve version constraints. See RFC | ||
5 | // TF-042 for details on this format. | ||
6 | type ModuleVersions struct { | ||
7 | Modules []*ModuleProviderVersions `json:"modules"` | ||
8 | } | ||
9 | |||
10 | // ModuleProviderVersions is the response format for a single module instance, | ||
11 | // containing metadata about all versions and their dependencies. | ||
12 | type ModuleProviderVersions struct { | ||
13 | Source string `json:"source"` | ||
14 | Versions []*ModuleVersion `json:"versions"` | ||
15 | } | ||
16 | |||
17 | // ModuleVersion is the output metadata for a given version needed by CLI to | ||
18 | // resolve candidate versions to satisfy requirements. | ||
19 | type ModuleVersion struct { | ||
20 | Version string `json:"version"` | ||
21 | Root VersionSubmodule `json:"root"` | ||
22 | Submodules []*VersionSubmodule `json:"submodules"` | ||
23 | } | ||
24 | |||
25 | // VersionSubmodule is the output metadata for a submodule within a given | ||
26 | // version needed by CLI to resolve candidate versions to satisfy requirements. | ||
27 | // When representing the Root in JSON the path is omitted. | ||
28 | type VersionSubmodule struct { | ||
29 | Path string `json:"path,omitempty"` | ||
30 | Providers []*ModuleProviderDep `json:"providers"` | ||
31 | Dependencies []*ModuleDep `json:"dependencies"` | ||
32 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/pagination.go b/vendor/github.com/hashicorp/terraform/registry/response/pagination.go new file mode 100644 index 0000000..75a9254 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/response/pagination.go | |||
@@ -0,0 +1,65 @@ | |||
1 | package response | ||
2 | |||
3 | import ( | ||
4 | "net/url" | ||
5 | "strconv" | ||
6 | ) | ||
7 | |||
8 | // PaginationMeta is a structure included in responses for pagination. | ||
9 | type PaginationMeta struct { | ||
10 | Limit int `json:"limit"` | ||
11 | CurrentOffset int `json:"current_offset"` | ||
12 | NextOffset *int `json:"next_offset,omitempty"` | ||
13 | PrevOffset *int `json:"prev_offset,omitempty"` | ||
14 | NextURL string `json:"next_url,omitempty"` | ||
15 | PrevURL string `json:"prev_url,omitempty"` | ||
16 | } | ||
17 | |||
18 | // NewPaginationMeta populates pagination meta data from result parameters | ||
19 | func NewPaginationMeta(offset, limit int, hasMore bool, currentURL string) PaginationMeta { | ||
20 | pm := PaginationMeta{ | ||
21 | Limit: limit, | ||
22 | CurrentOffset: offset, | ||
23 | } | ||
24 | |||
25 | // Calculate next/prev offsets, leave nil if not valid pages | ||
26 | nextOffset := offset + limit | ||
27 | if hasMore { | ||
28 | pm.NextOffset = &nextOffset | ||
29 | } | ||
30 | |||
31 | prevOffset := offset - limit | ||
32 | if prevOffset < 0 { | ||
33 | prevOffset = 0 | ||
34 | } | ||
35 | if prevOffset < offset { | ||
36 | pm.PrevOffset = &prevOffset | ||
37 | } | ||
38 | |||
39 | // If URL format provided, populate URLs. Intentionally swallow URL errors for now, API should | ||
40 | // catch missing URLs if we call with bad URL arg (and we care about them being present). | ||
41 | if currentURL != "" && pm.NextOffset != nil { | ||
42 | pm.NextURL, _ = setQueryParam(currentURL, "offset", *pm.NextOffset, 0) | ||
43 | } | ||
44 | if currentURL != "" && pm.PrevOffset != nil { | ||
45 | pm.PrevURL, _ = setQueryParam(currentURL, "offset", *pm.PrevOffset, 0) | ||
46 | } | ||
47 | |||
48 | return pm | ||
49 | } | ||
50 | |||
51 | func setQueryParam(baseURL, key string, val, defaultVal int) (string, error) { | ||
52 | u, err := url.Parse(baseURL) | ||
53 | if err != nil { | ||
54 | return "", err | ||
55 | } | ||
56 | q := u.Query() | ||
57 | if val == defaultVal { | ||
58 | // elide param if it's the default value | ||
59 | q.Del(key) | ||
60 | } else { | ||
61 | q.Set(key, strconv.Itoa(val)) | ||
62 | } | ||
63 | u.RawQuery = q.Encode() | ||
64 | return u.String(), nil | ||
65 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/registry/response/redirect.go b/vendor/github.com/hashicorp/terraform/registry/response/redirect.go new file mode 100644 index 0000000..d5eb49b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/registry/response/redirect.go | |||
@@ -0,0 +1,6 @@ | |||
1 | package response | ||
2 | |||
3 | // Redirect causes the frontend to perform a window redirect. | ||
4 | type Redirect struct { | ||
5 | URL string `json:"url"` | ||
6 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/cache.go b/vendor/github.com/hashicorp/terraform/svchost/auth/cache.go new file mode 100644 index 0000000..4f0d168 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/auth/cache.go | |||
@@ -0,0 +1,45 @@ | |||
1 | package auth | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/terraform/svchost" | ||
5 | ) | ||
6 | |||
7 | // CachingCredentialsSource creates a new credentials source that wraps another | ||
8 | // and caches its results in memory, on a per-hostname basis. | ||
9 | // | ||
10 | // No means is provided for expiration of cached credentials, so a caching | ||
11 | // credentials source should have a limited lifetime (one Terraform operation, | ||
12 | // for example) to ensure that time-limited credentials don't expire before | ||
13 | // their cache entries do. | ||
14 | func CachingCredentialsSource(source CredentialsSource) CredentialsSource { | ||
15 | return &cachingCredentialsSource{ | ||
16 | source: source, | ||
17 | cache: map[svchost.Hostname]HostCredentials{}, | ||
18 | } | ||
19 | } | ||
20 | |||
21 | type cachingCredentialsSource struct { | ||
22 | source CredentialsSource | ||
23 | cache map[svchost.Hostname]HostCredentials | ||
24 | } | ||
25 | |||
26 | // ForHost passes the given hostname on to the wrapped credentials source and | ||
27 | // caches the result to return for future requests with the same hostname. | ||
28 | // | ||
29 | // Both credentials and non-credentials (nil) responses are cached. | ||
30 | // | ||
31 | // No cache entry is created if the wrapped source returns an error, to allow | ||
32 | // the caller to retry the failing operation. | ||
33 | func (s *cachingCredentialsSource) ForHost(host svchost.Hostname) (HostCredentials, error) { | ||
34 | if cache, cached := s.cache[host]; cached { | ||
35 | return cache, nil | ||
36 | } | ||
37 | |||
38 | result, err := s.source.ForHost(host) | ||
39 | if err != nil { | ||
40 | return result, err | ||
41 | } | ||
42 | |||
43 | s.cache[host] = result | ||
44 | return result, nil | ||
45 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/credentials.go b/vendor/github.com/hashicorp/terraform/svchost/auth/credentials.go new file mode 100644 index 0000000..0372c16 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/auth/credentials.go | |||
@@ -0,0 +1,63 @@ | |||
1 | // Package auth contains types and functions to manage authentication | ||
2 | // credentials for service hosts. | ||
3 | package auth | ||
4 | |||
5 | import ( | ||
6 | "net/http" | ||
7 | |||
8 | "github.com/hashicorp/terraform/svchost" | ||
9 | ) | ||
10 | |||
11 | // Credentials is a list of CredentialsSource objects that can be tried in | ||
12 | // turn until one returns credentials for a host, or one returns an error. | ||
13 | // | ||
14 | // A Credentials is itself a CredentialsSource, wrapping its members. | ||
15 | // In principle one CredentialsSource can be nested inside another, though | ||
16 | // there is no good reason to do so. | ||
17 | type Credentials []CredentialsSource | ||
18 | |||
19 | // NoCredentials is an empty CredentialsSource that always returns nil | ||
20 | // when asked for credentials. | ||
21 | var NoCredentials CredentialsSource = Credentials{} | ||
22 | |||
23 | // A CredentialsSource is an object that may be able to provide credentials | ||
24 | // for a given host. | ||
25 | // | ||
26 | // Credentials lookups are not guaranteed to be concurrency-safe. Callers | ||
27 | // using these facilities in concurrent code must use external concurrency | ||
28 | // primitives to prevent race conditions. | ||
29 | type CredentialsSource interface { | ||
30 | // ForHost returns a non-nil HostCredentials if the source has credentials | ||
31 | // available for the host, and a nil HostCredentials if it does not. | ||
32 | // | ||
33 | // If an error is returned, progress through a list of CredentialsSources | ||
34 | // is halted and the error is returned to the user. | ||
35 | ForHost(host svchost.Hostname) (HostCredentials, error) | ||
36 | } | ||
37 | |||
38 | // HostCredentials represents a single set of credentials for a particular | ||
39 | // host. | ||
40 | type HostCredentials interface { | ||
41 | // PrepareRequest modifies the given request in-place to apply the | ||
42 | // receiving credentials. The usual behavior of this method is to | ||
43 | // add some sort of Authorization header to the request. | ||
44 | PrepareRequest(req *http.Request) | ||
45 | |||
46 | // Token returns the authentication token. | ||
47 | Token() string | ||
48 | } | ||
49 | |||
50 | // ForHost iterates over the contained CredentialsSource objects and | ||
51 | // tries to obtain credentials for the given host from each one in turn. | ||
52 | // | ||
53 | // If any source returns either a non-nil HostCredentials or a non-nil error | ||
54 | // then this result is returned. Otherwise, the result is nil, nil. | ||
55 | func (c Credentials) ForHost(host svchost.Hostname) (HostCredentials, error) { | ||
56 | for _, source := range c { | ||
57 | creds, err := source.ForHost(host) | ||
58 | if creds != nil || err != nil { | ||
59 | return creds, err | ||
60 | } | ||
61 | } | ||
62 | return nil, nil | ||
63 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/from_map.go b/vendor/github.com/hashicorp/terraform/svchost/auth/from_map.go new file mode 100644 index 0000000..f91006a --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/auth/from_map.go | |||
@@ -0,0 +1,18 @@ | |||
1 | package auth | ||
2 | |||
3 | // HostCredentialsFromMap converts a map of key-value pairs from a credentials | ||
4 | // definition provided by the user (e.g. in a config file, or via a credentials | ||
5 | // helper) into a HostCredentials object if possible, or returns nil if | ||
6 | // no credentials could be extracted from the map. | ||
7 | // | ||
8 | // This function ignores map keys it is unfamiliar with, to allow for future | ||
9 | // expansion of the credentials map format for new credential types. | ||
10 | func HostCredentialsFromMap(m map[string]interface{}) HostCredentials { | ||
11 | if m == nil { | ||
12 | return nil | ||
13 | } | ||
14 | if token, ok := m["token"].(string); ok { | ||
15 | return HostCredentialsToken(token) | ||
16 | } | ||
17 | return nil | ||
18 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/helper_program.go b/vendor/github.com/hashicorp/terraform/svchost/auth/helper_program.go new file mode 100644 index 0000000..d72ffe3 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/auth/helper_program.go | |||
@@ -0,0 +1,80 @@ | |||
1 | package auth | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "encoding/json" | ||
6 | "fmt" | ||
7 | "os/exec" | ||
8 | "path/filepath" | ||
9 | |||
10 | "github.com/hashicorp/terraform/svchost" | ||
11 | ) | ||
12 | |||
13 | type helperProgramCredentialsSource struct { | ||
14 | executable string | ||
15 | args []string | ||
16 | } | ||
17 | |||
18 | // HelperProgramCredentialsSource returns a CredentialsSource that runs the | ||
19 | // given program with the given arguments in order to obtain credentials. | ||
20 | // | ||
21 | // The given executable path must be an absolute path; it is the caller's | ||
22 | // responsibility to validate and process a relative path or other input | ||
23 | // provided by an end-user. If the given path is not absolute, this | ||
24 | // function will panic. | ||
25 | // | ||
26 | // When credentials are requested, the program will be run in a child process | ||
27 | // with the given arguments along with two additional arguments added to the | ||
28 | // end of the list: the literal string "get", followed by the requested | ||
29 | // hostname in ASCII compatibility form (punycode form). | ||
30 | func HelperProgramCredentialsSource(executable string, args ...string) CredentialsSource { | ||
31 | if !filepath.IsAbs(executable) { | ||
32 | panic("NewCredentialsSourceHelperProgram requires absolute path to executable") | ||
33 | } | ||
34 | |||
35 | fullArgs := make([]string, len(args)+1) | ||
36 | fullArgs[0] = executable | ||
37 | copy(fullArgs[1:], args) | ||
38 | |||
39 | return &helperProgramCredentialsSource{ | ||
40 | executable: executable, | ||
41 | args: fullArgs, | ||
42 | } | ||
43 | } | ||
44 | |||
45 | func (s *helperProgramCredentialsSource) ForHost(host svchost.Hostname) (HostCredentials, error) { | ||
46 | args := make([]string, len(s.args), len(s.args)+2) | ||
47 | copy(args, s.args) | ||
48 | args = append(args, "get") | ||
49 | args = append(args, string(host)) | ||
50 | |||
51 | outBuf := bytes.Buffer{} | ||
52 | errBuf := bytes.Buffer{} | ||
53 | |||
54 | cmd := exec.Cmd{ | ||
55 | Path: s.executable, | ||
56 | Args: args, | ||
57 | Stdin: nil, | ||
58 | Stdout: &outBuf, | ||
59 | Stderr: &errBuf, | ||
60 | } | ||
61 | err := cmd.Run() | ||
62 | if _, isExitErr := err.(*exec.ExitError); isExitErr { | ||
63 | errText := errBuf.String() | ||
64 | if errText == "" { | ||
65 | // Shouldn't happen for a well-behaved helper program | ||
66 | return nil, fmt.Errorf("error in %s, but it produced no error message", s.executable) | ||
67 | } | ||
68 | return nil, fmt.Errorf("error in %s: %s", s.executable, errText) | ||
69 | } else if err != nil { | ||
70 | return nil, fmt.Errorf("failed to run %s: %s", s.executable, err) | ||
71 | } | ||
72 | |||
73 | var m map[string]interface{} | ||
74 | err = json.Unmarshal(outBuf.Bytes(), &m) | ||
75 | if err != nil { | ||
76 | return nil, fmt.Errorf("malformed output from %s: %s", s.executable, err) | ||
77 | } | ||
78 | |||
79 | return HostCredentialsFromMap(m), nil | ||
80 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/static.go b/vendor/github.com/hashicorp/terraform/svchost/auth/static.go new file mode 100644 index 0000000..5373fdd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/auth/static.go | |||
@@ -0,0 +1,28 @@ | |||
1 | package auth | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/terraform/svchost" | ||
5 | ) | ||
6 | |||
7 | // StaticCredentialsSource is a credentials source that retrieves credentials | ||
8 | // from the provided map. It returns nil if a requested hostname is not | ||
9 | // present in the map. | ||
10 | // | ||
11 | // The caller should not modify the given map after passing it to this function. | ||
12 | func StaticCredentialsSource(creds map[svchost.Hostname]map[string]interface{}) CredentialsSource { | ||
13 | return staticCredentialsSource(creds) | ||
14 | } | ||
15 | |||
16 | type staticCredentialsSource map[svchost.Hostname]map[string]interface{} | ||
17 | |||
18 | func (s staticCredentialsSource) ForHost(host svchost.Hostname) (HostCredentials, error) { | ||
19 | if s == nil { | ||
20 | return nil, nil | ||
21 | } | ||
22 | |||
23 | if m, exists := s[host]; exists { | ||
24 | return HostCredentialsFromMap(m), nil | ||
25 | } | ||
26 | |||
27 | return nil, nil | ||
28 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/auth/token_credentials.go b/vendor/github.com/hashicorp/terraform/svchost/auth/token_credentials.go new file mode 100644 index 0000000..9358bcb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/auth/token_credentials.go | |||
@@ -0,0 +1,25 @@ | |||
1 | package auth | ||
2 | |||
3 | import ( | ||
4 | "net/http" | ||
5 | ) | ||
6 | |||
7 | // HostCredentialsToken is a HostCredentials implementation that represents a | ||
8 | // single "bearer token", to be sent to the server via an Authorization header | ||
9 | // with the auth type set to "Bearer" | ||
10 | type HostCredentialsToken string | ||
11 | |||
12 | // PrepareRequest alters the given HTTP request by setting its Authorization | ||
13 | // header to the string "Bearer " followed by the encapsulated authentication | ||
14 | // token. | ||
15 | func (tc HostCredentialsToken) PrepareRequest(req *http.Request) { | ||
16 | if req.Header == nil { | ||
17 | req.Header = http.Header{} | ||
18 | } | ||
19 | req.Header.Set("Authorization", "Bearer "+string(tc)) | ||
20 | } | ||
21 | |||
22 | // Token returns the authentication token. | ||
23 | func (tc HostCredentialsToken) Token() string { | ||
24 | return string(tc) | ||
25 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/disco/disco.go b/vendor/github.com/hashicorp/terraform/svchost/disco/disco.go new file mode 100644 index 0000000..1963cbd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/disco/disco.go | |||
@@ -0,0 +1,259 @@ | |||
1 | // Package disco handles Terraform's remote service discovery protocol. | ||
2 | // | ||
3 | // This protocol allows mapping from a service hostname, as produced by the | ||
4 | // svchost package, to a set of services supported by that host and the | ||
5 | // endpoint information for each supported service. | ||
6 | package disco | ||
7 | |||
8 | import ( | ||
9 | "encoding/json" | ||
10 | "errors" | ||
11 | "fmt" | ||
12 | "io" | ||
13 | "io/ioutil" | ||
14 | "log" | ||
15 | "mime" | ||
16 | "net/http" | ||
17 | "net/url" | ||
18 | "time" | ||
19 | |||
20 | cleanhttp "github.com/hashicorp/go-cleanhttp" | ||
21 | "github.com/hashicorp/terraform/httpclient" | ||
22 | "github.com/hashicorp/terraform/svchost" | ||
23 | "github.com/hashicorp/terraform/svchost/auth" | ||
24 | ) | ||
25 | |||
26 | const ( | ||
27 | // Fixed path to the discovery manifest. | ||
28 | discoPath = "/.well-known/terraform.json" | ||
29 | |||
30 | // Arbitrary-but-small number to prevent runaway redirect loops. | ||
31 | maxRedirects = 3 | ||
32 | |||
33 | // Arbitrary-but-small time limit to prevent UI "hangs" during discovery. | ||
34 | discoTimeout = 11 * time.Second | ||
35 | |||
36 | // 1MB - to prevent abusive services from using loads of our memory. | ||
37 | maxDiscoDocBytes = 1 * 1024 * 1024 | ||
38 | ) | ||
39 | |||
40 | // httpTransport is overridden during tests, to skip TLS verification. | ||
41 | var httpTransport = cleanhttp.DefaultPooledTransport() | ||
42 | |||
43 | // Disco is the main type in this package, which allows discovery on given | ||
44 | // hostnames and caches the results by hostname to avoid repeated requests | ||
45 | // for the same information. | ||
46 | type Disco struct { | ||
47 | hostCache map[svchost.Hostname]*Host | ||
48 | credsSrc auth.CredentialsSource | ||
49 | |||
50 | // Transport is a custom http.RoundTripper to use. | ||
51 | Transport http.RoundTripper | ||
52 | } | ||
53 | |||
54 | // New returns a new initialized discovery object. | ||
55 | func New() *Disco { | ||
56 | return NewWithCredentialsSource(nil) | ||
57 | } | ||
58 | |||
59 | // NewWithCredentialsSource returns a new discovery object initialized with | ||
60 | // the given credentials source. | ||
61 | func NewWithCredentialsSource(credsSrc auth.CredentialsSource) *Disco { | ||
62 | return &Disco{ | ||
63 | hostCache: make(map[svchost.Hostname]*Host), | ||
64 | credsSrc: credsSrc, | ||
65 | Transport: httpTransport, | ||
66 | } | ||
67 | } | ||
68 | |||
69 | // SetCredentialsSource provides a credentials source that will be used to | ||
70 | // add credentials to outgoing discovery requests, where available. | ||
71 | // | ||
72 | // If this method is never called, no outgoing discovery requests will have | ||
73 | // credentials. | ||
74 | func (d *Disco) SetCredentialsSource(src auth.CredentialsSource) { | ||
75 | d.credsSrc = src | ||
76 | } | ||
77 | |||
78 | // CredentialsForHost returns a non-nil HostCredentials if the embedded source has | ||
79 | // credentials available for the host, and a nil HostCredentials if it does not. | ||
80 | func (d *Disco) CredentialsForHost(hostname svchost.Hostname) (auth.HostCredentials, error) { | ||
81 | if d.credsSrc == nil { | ||
82 | return nil, nil | ||
83 | } | ||
84 | return d.credsSrc.ForHost(hostname) | ||
85 | } | ||
86 | |||
87 | // ForceHostServices provides a pre-defined set of services for a given | ||
88 | // host, which prevents the receiver from attempting network-based discovery | ||
89 | // for the given host. Instead, the given services map will be returned | ||
90 | // verbatim. | ||
91 | // | ||
92 | // When providing "forced" services, any relative URLs are resolved against | ||
93 | // the initial discovery URL that would have been used for network-based | ||
94 | // discovery, yielding the same results as if the given map were published | ||
95 | // at the host's default discovery URL, though using absolute URLs is strongly | ||
96 | // recommended to make the configured behavior more explicit. | ||
97 | func (d *Disco) ForceHostServices(hostname svchost.Hostname, services map[string]interface{}) { | ||
98 | if services == nil { | ||
99 | services = map[string]interface{}{} | ||
100 | } | ||
101 | |||
102 | d.hostCache[hostname] = &Host{ | ||
103 | discoURL: &url.URL{ | ||
104 | Scheme: "https", | ||
105 | Host: string(hostname), | ||
106 | Path: discoPath, | ||
107 | }, | ||
108 | hostname: hostname.ForDisplay(), | ||
109 | services: services, | ||
110 | transport: d.Transport, | ||
111 | } | ||
112 | } | ||
113 | |||
114 | // Discover runs the discovery protocol against the given hostname (which must | ||
115 | // already have been validated and prepared with svchost.ForComparison) and | ||
116 | // returns an object describing the services available at that host. | ||
117 | // | ||
118 | // If a given hostname supports no Terraform services at all, a non-nil but | ||
119 | // empty Host object is returned. When giving feedback to the end user about | ||
120 | // such situations, we say "host <name> does not provide a <service> service", | ||
121 | // regardless of whether that is due to that service specifically being absent | ||
122 | // or due to the host not providing Terraform services at all, since we don't | ||
123 | // wish to expose the detail of whole-host discovery to an end-user. | ||
124 | func (d *Disco) Discover(hostname svchost.Hostname) (*Host, error) { | ||
125 | if host, cached := d.hostCache[hostname]; cached { | ||
126 | return host, nil | ||
127 | } | ||
128 | |||
129 | host, err := d.discover(hostname) | ||
130 | if err != nil { | ||
131 | return nil, err | ||
132 | } | ||
133 | d.hostCache[hostname] = host | ||
134 | |||
135 | return host, nil | ||
136 | } | ||
137 | |||
138 | // DiscoverServiceURL is a convenience wrapper for discovery on a given | ||
139 | // hostname and then looking up a particular service in the result. | ||
140 | func (d *Disco) DiscoverServiceURL(hostname svchost.Hostname, serviceID string) (*url.URL, error) { | ||
141 | host, err := d.Discover(hostname) | ||
142 | if err != nil { | ||
143 | return nil, err | ||
144 | } | ||
145 | return host.ServiceURL(serviceID) | ||
146 | } | ||
147 | |||
148 | // discover implements the actual discovery process, with its result cached | ||
149 | // by the public-facing Discover method. | ||
150 | func (d *Disco) discover(hostname svchost.Hostname) (*Host, error) { | ||
151 | discoURL := &url.URL{ | ||
152 | Scheme: "https", | ||
153 | Host: hostname.String(), | ||
154 | Path: discoPath, | ||
155 | } | ||
156 | |||
157 | client := &http.Client{ | ||
158 | Transport: d.Transport, | ||
159 | Timeout: discoTimeout, | ||
160 | |||
161 | CheckRedirect: func(req *http.Request, via []*http.Request) error { | ||
162 | log.Printf("[DEBUG] Service discovery redirected to %s", req.URL) | ||
163 | if len(via) > maxRedirects { | ||
164 | return errors.New("too many redirects") // this error will never actually be seen | ||
165 | } | ||
166 | return nil | ||
167 | }, | ||
168 | } | ||
169 | |||
170 | req := &http.Request{ | ||
171 | Header: make(http.Header), | ||
172 | Method: "GET", | ||
173 | URL: discoURL, | ||
174 | } | ||
175 | req.Header.Set("Accept", "application/json") | ||
176 | req.Header.Set("User-Agent", httpclient.UserAgentString()) | ||
177 | |||
178 | creds, err := d.CredentialsForHost(hostname) | ||
179 | if err != nil { | ||
180 | log.Printf("[WARN] Failed to get credentials for %s: %s (ignoring)", hostname, err) | ||
181 | } | ||
182 | if creds != nil { | ||
183 | // Update the request to include credentials. | ||
184 | creds.PrepareRequest(req) | ||
185 | } | ||
186 | |||
187 | log.Printf("[DEBUG] Service discovery for %s at %s", hostname, discoURL) | ||
188 | |||
189 | resp, err := client.Do(req) | ||
190 | if err != nil { | ||
191 | return nil, fmt.Errorf("Failed to request discovery document: %v", err) | ||
192 | } | ||
193 | defer resp.Body.Close() | ||
194 | |||
195 | host := &Host{ | ||
196 | // Use the discovery URL from resp.Request in | ||
197 | // case the client followed any redirects. | ||
198 | discoURL: resp.Request.URL, | ||
199 | hostname: hostname.ForDisplay(), | ||
200 | transport: d.Transport, | ||
201 | } | ||
202 | |||
203 | // Return the host without any services. | ||
204 | if resp.StatusCode == 404 { | ||
205 | return host, nil | ||
206 | } | ||
207 | |||
208 | if resp.StatusCode != 200 { | ||
209 | return nil, fmt.Errorf("Failed to request discovery document: %s", resp.Status) | ||
210 | } | ||
211 | |||
212 | contentType := resp.Header.Get("Content-Type") | ||
213 | mediaType, _, err := mime.ParseMediaType(contentType) | ||
214 | if err != nil { | ||
215 | return nil, fmt.Errorf("Discovery URL has a malformed Content-Type %q", contentType) | ||
216 | } | ||
217 | if mediaType != "application/json" { | ||
218 | return nil, fmt.Errorf("Discovery URL returned an unsupported Content-Type %q", mediaType) | ||
219 | } | ||
220 | |||
221 | // This doesn't catch chunked encoding, because ContentLength is -1 in that case. | ||
222 | if resp.ContentLength > maxDiscoDocBytes { | ||
223 | // Size limit here is not a contractual requirement and so we may | ||
224 | // adjust it over time if we find a different limit is warranted. | ||
225 | return nil, fmt.Errorf( | ||
226 | "Discovery doc response is too large (got %d bytes; limit %d)", | ||
227 | resp.ContentLength, maxDiscoDocBytes, | ||
228 | ) | ||
229 | } | ||
230 | |||
231 | // If the response is using chunked encoding then we can't predict its | ||
232 | // size, but we'll at least prevent reading the entire thing into memory. | ||
233 | lr := io.LimitReader(resp.Body, maxDiscoDocBytes) | ||
234 | |||
235 | servicesBytes, err := ioutil.ReadAll(lr) | ||
236 | if err != nil { | ||
237 | return nil, fmt.Errorf("Error reading discovery document body: %v", err) | ||
238 | } | ||
239 | |||
240 | var services map[string]interface{} | ||
241 | err = json.Unmarshal(servicesBytes, &services) | ||
242 | if err != nil { | ||
243 | return nil, fmt.Errorf("Failed to decode discovery document as a JSON object: %v", err) | ||
244 | } | ||
245 | host.services = services | ||
246 | |||
247 | return host, nil | ||
248 | } | ||
249 | |||
250 | // Forget invalidates any cached record of the given hostname. If the host | ||
251 | // has no cache entry then this is a no-op. | ||
252 | func (d *Disco) Forget(hostname svchost.Hostname) { | ||
253 | delete(d.hostCache, hostname) | ||
254 | } | ||
255 | |||
256 | // ForgetAll is like Forget, but for all of the hostnames that have cache entries. | ||
257 | func (d *Disco) ForgetAll() { | ||
258 | d.hostCache = make(map[svchost.Hostname]*Host) | ||
259 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/disco/host.go b/vendor/github.com/hashicorp/terraform/svchost/disco/host.go new file mode 100644 index 0000000..ab9514c --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/disco/host.go | |||
@@ -0,0 +1,264 @@ | |||
1 | package disco | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | "log" | ||
7 | "net/http" | ||
8 | "net/url" | ||
9 | "os" | ||
10 | "strconv" | ||
11 | "strings" | ||
12 | "time" | ||
13 | |||
14 | "github.com/hashicorp/go-version" | ||
15 | "github.com/hashicorp/terraform/httpclient" | ||
16 | ) | ||
17 | |||
18 | const versionServiceID = "versions.v1" | ||
19 | |||
20 | // Host represents a service discovered host. | ||
21 | type Host struct { | ||
22 | discoURL *url.URL | ||
23 | hostname string | ||
24 | services map[string]interface{} | ||
25 | transport http.RoundTripper | ||
26 | } | ||
27 | |||
28 | // Constraints represents the version constraints of a service. | ||
29 | type Constraints struct { | ||
30 | Service string `json:"service"` | ||
31 | Product string `json:"product"` | ||
32 | Minimum string `json:"minimum"` | ||
33 | Maximum string `json:"maximum"` | ||
34 | Excluding []string `json:"excluding"` | ||
35 | } | ||
36 | |||
37 | // ErrServiceNotProvided is returned when the service is not provided. | ||
38 | type ErrServiceNotProvided struct { | ||
39 | hostname string | ||
40 | service string | ||
41 | } | ||
42 | |||
43 | // Error returns a customized error message. | ||
44 | func (e *ErrServiceNotProvided) Error() string { | ||
45 | if e.hostname == "" { | ||
46 | return fmt.Sprintf("host does not provide a %s service", e.service) | ||
47 | } | ||
48 | return fmt.Sprintf("host %s does not provide a %s service", e.hostname, e.service) | ||
49 | } | ||
50 | |||
51 | // ErrVersionNotSupported is returned when the version is not supported. | ||
52 | type ErrVersionNotSupported struct { | ||
53 | hostname string | ||
54 | service string | ||
55 | version string | ||
56 | } | ||
57 | |||
58 | // Error returns a customized error message. | ||
59 | func (e *ErrVersionNotSupported) Error() string { | ||
60 | if e.hostname == "" { | ||
61 | return fmt.Sprintf("host does not support %s version %s", e.service, e.version) | ||
62 | } | ||
63 | return fmt.Sprintf("host %s does not support %s version %s", e.hostname, e.service, e.version) | ||
64 | } | ||
65 | |||
66 | // ErrNoVersionConstraints is returned when checkpoint was disabled | ||
67 | // or the endpoint to query for version constraints was unavailable. | ||
68 | type ErrNoVersionConstraints struct { | ||
69 | disabled bool | ||
70 | } | ||
71 | |||
72 | // Error returns a customized error message. | ||
73 | func (e *ErrNoVersionConstraints) Error() string { | ||
74 | if e.disabled { | ||
75 | return "checkpoint disabled" | ||
76 | } | ||
77 | return "unable to contact versions service" | ||
78 | } | ||
79 | |||
80 | // ServiceURL returns the URL associated with the given service identifier, | ||
81 | // which should be of the form "servicename.vN". | ||
82 | // | ||
83 | // A non-nil result is always an absolute URL with a scheme of either HTTPS | ||
84 | // or HTTP. | ||
85 | func (h *Host) ServiceURL(id string) (*url.URL, error) { | ||
86 | svc, ver, err := parseServiceID(id) | ||
87 | if err != nil { | ||
88 | return nil, err | ||
89 | } | ||
90 | |||
91 | // No services supported for an empty Host. | ||
92 | if h == nil || h.services == nil { | ||
93 | return nil, &ErrServiceNotProvided{service: svc} | ||
94 | } | ||
95 | |||
96 | urlStr, ok := h.services[id].(string) | ||
97 | if !ok { | ||
98 | // See if we have a matching service as that would indicate | ||
99 | // the service is supported, but not the requested version. | ||
100 | for serviceID := range h.services { | ||
101 | if strings.HasPrefix(serviceID, svc+".") { | ||
102 | return nil, &ErrVersionNotSupported{ | ||
103 | hostname: h.hostname, | ||
104 | service: svc, | ||
105 | version: ver.Original(), | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | // No discovered services match the requested service. | ||
111 | return nil, &ErrServiceNotProvided{hostname: h.hostname, service: svc} | ||
112 | } | ||
113 | |||
114 | u, err := url.Parse(urlStr) | ||
115 | if err != nil { | ||
116 | return nil, fmt.Errorf("Failed to parse service URL: %v", err) | ||
117 | } | ||
118 | |||
119 | // Make relative URLs absolute using our discovery URL. | ||
120 | if !u.IsAbs() { | ||
121 | u = h.discoURL.ResolveReference(u) | ||
122 | } | ||
123 | |||
124 | if u.Scheme != "https" && u.Scheme != "http" { | ||
125 | return nil, fmt.Errorf("Service URL is using an unsupported scheme: %s", u.Scheme) | ||
126 | } | ||
127 | if u.User != nil { | ||
128 | return nil, fmt.Errorf("Embedded username/password information is not permitted") | ||
129 | } | ||
130 | |||
131 | // Fragment part is irrelevant, since we're not a browser. | ||
132 | u.Fragment = "" | ||
133 | |||
134 | return h.discoURL.ResolveReference(u), nil | ||
135 | } | ||
136 | |||
137 | // VersionConstraints returns the contraints for a given service identifier | ||
138 | // (which should be of the form "servicename.vN") and product. | ||
139 | // | ||
140 | // When an exact (service and version) match is found, the constraints for | ||
141 | // that service are returned. | ||
142 | // | ||
143 | // When the requested version is not provided but the service is, we will | ||
144 | // search for all alternative versions. If mutliple alternative versions | ||
145 | // are found, the contrains of the latest available version are returned. | ||
146 | // | ||
147 | // When a service is not provided at all an error will be returned instead. | ||
148 | // | ||
149 | // When checkpoint is disabled or when a 404 is returned after making the | ||
150 | // HTTP call, an ErrNoVersionConstraints error will be returned. | ||
151 | func (h *Host) VersionConstraints(id, product string) (*Constraints, error) { | ||
152 | svc, _, err := parseServiceID(id) | ||
153 | if err != nil { | ||
154 | return nil, err | ||
155 | } | ||
156 | |||
157 | // Return early if checkpoint is disabled. | ||
158 | if disabled := os.Getenv("CHECKPOINT_DISABLE"); disabled != "" { | ||
159 | return nil, &ErrNoVersionConstraints{disabled: true} | ||
160 | } | ||
161 | |||
162 | // No services supported for an empty Host. | ||
163 | if h == nil || h.services == nil { | ||
164 | return nil, &ErrServiceNotProvided{service: svc} | ||
165 | } | ||
166 | |||
167 | // Try to get the service URL for the version service and | ||
168 | // return early if the service isn't provided by the host. | ||
169 | u, err := h.ServiceURL(versionServiceID) | ||
170 | if err != nil { | ||
171 | return nil, err | ||
172 | } | ||
173 | |||
174 | // Check if we have an exact (service and version) match. | ||
175 | if _, ok := h.services[id].(string); !ok { | ||
176 | // If we don't have an exact match, we search for all matching | ||
177 | // services and then use the service ID of the latest version. | ||
178 | var services []string | ||
179 | for serviceID := range h.services { | ||
180 | if strings.HasPrefix(serviceID, svc+".") { | ||
181 | services = append(services, serviceID) | ||
182 | } | ||
183 | } | ||
184 | |||
185 | if len(services) == 0 { | ||
186 | // No discovered services match the requested service. | ||
187 | return nil, &ErrServiceNotProvided{hostname: h.hostname, service: svc} | ||
188 | } | ||
189 | |||
190 | // Set id to the latest service ID we found. | ||
191 | var latest *version.Version | ||
192 | for _, serviceID := range services { | ||
193 | if _, ver, err := parseServiceID(serviceID); err == nil { | ||
194 | if latest == nil || latest.LessThan(ver) { | ||
195 | id = serviceID | ||
196 | latest = ver | ||
197 | } | ||
198 | } | ||
199 | } | ||
200 | } | ||
201 | |||
202 | // Set a default timeout of 1 sec for the versions request (in milliseconds) | ||
203 | timeout := 1000 | ||
204 | if v, err := strconv.Atoi(os.Getenv("CHECKPOINT_TIMEOUT")); err == nil { | ||
205 | timeout = v | ||
206 | } | ||
207 | |||
208 | client := &http.Client{ | ||
209 | Transport: h.transport, | ||
210 | Timeout: time.Duration(timeout) * time.Millisecond, | ||
211 | } | ||
212 | |||
213 | // Prepare the service URL by setting the service and product. | ||
214 | v := u.Query() | ||
215 | v.Set("product", product) | ||
216 | u.Path += id | ||
217 | u.RawQuery = v.Encode() | ||
218 | |||
219 | // Create a new request. | ||
220 | req, err := http.NewRequest("GET", u.String(), nil) | ||
221 | if err != nil { | ||
222 | return nil, fmt.Errorf("Failed to create version constraints request: %v", err) | ||
223 | } | ||
224 | req.Header.Set("Accept", "application/json") | ||
225 | req.Header.Set("User-Agent", httpclient.UserAgentString()) | ||
226 | |||
227 | log.Printf("[DEBUG] Retrieve version constraints for service %s and product %s", id, product) | ||
228 | |||
229 | resp, err := client.Do(req) | ||
230 | if err != nil { | ||
231 | return nil, fmt.Errorf("Failed to request version constraints: %v", err) | ||
232 | } | ||
233 | defer resp.Body.Close() | ||
234 | |||
235 | if resp.StatusCode == 404 { | ||
236 | return nil, &ErrNoVersionConstraints{disabled: false} | ||
237 | } | ||
238 | |||
239 | if resp.StatusCode != 200 { | ||
240 | return nil, fmt.Errorf("Failed to request version constraints: %s", resp.Status) | ||
241 | } | ||
242 | |||
243 | // Parse the constraints from the response body. | ||
244 | result := &Constraints{} | ||
245 | if err := json.NewDecoder(resp.Body).Decode(result); err != nil { | ||
246 | return nil, fmt.Errorf("Error parsing version constraints: %v", err) | ||
247 | } | ||
248 | |||
249 | return result, nil | ||
250 | } | ||
251 | |||
252 | func parseServiceID(id string) (string, *version.Version, error) { | ||
253 | parts := strings.SplitN(id, ".", 2) | ||
254 | if len(parts) != 2 { | ||
255 | return "", nil, fmt.Errorf("Invalid service ID format (i.e. service.vN): %s", id) | ||
256 | } | ||
257 | |||
258 | version, err := version.NewVersion(parts[1]) | ||
259 | if err != nil { | ||
260 | return "", nil, fmt.Errorf("Invalid service version: %v", err) | ||
261 | } | ||
262 | |||
263 | return parts[0], version, nil | ||
264 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/label_iter.go b/vendor/github.com/hashicorp/terraform/svchost/label_iter.go new file mode 100644 index 0000000..af8ccba --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/label_iter.go | |||
@@ -0,0 +1,69 @@ | |||
1 | package svchost | ||
2 | |||
3 | import ( | ||
4 | "strings" | ||
5 | ) | ||
6 | |||
7 | // A labelIter allows iterating over domain name labels. | ||
8 | // | ||
9 | // This type is copied from golang.org/x/net/idna, where it is used | ||
10 | // to segment hostnames into their separate labels for analysis. We use | ||
11 | // it for the same purpose here, in ForComparison. | ||
12 | type labelIter struct { | ||
13 | orig string | ||
14 | slice []string | ||
15 | curStart int | ||
16 | curEnd int | ||
17 | i int | ||
18 | } | ||
19 | |||
20 | func (l *labelIter) reset() { | ||
21 | l.curStart = 0 | ||
22 | l.curEnd = 0 | ||
23 | l.i = 0 | ||
24 | } | ||
25 | |||
26 | func (l *labelIter) done() bool { | ||
27 | return l.curStart >= len(l.orig) | ||
28 | } | ||
29 | |||
30 | func (l *labelIter) result() string { | ||
31 | if l.slice != nil { | ||
32 | return strings.Join(l.slice, ".") | ||
33 | } | ||
34 | return l.orig | ||
35 | } | ||
36 | |||
37 | func (l *labelIter) label() string { | ||
38 | if l.slice != nil { | ||
39 | return l.slice[l.i] | ||
40 | } | ||
41 | p := strings.IndexByte(l.orig[l.curStart:], '.') | ||
42 | l.curEnd = l.curStart + p | ||
43 | if p == -1 { | ||
44 | l.curEnd = len(l.orig) | ||
45 | } | ||
46 | return l.orig[l.curStart:l.curEnd] | ||
47 | } | ||
48 | |||
49 | // next sets the value to the next label. It skips the last label if it is empty. | ||
50 | func (l *labelIter) next() { | ||
51 | l.i++ | ||
52 | if l.slice != nil { | ||
53 | if l.i >= len(l.slice) || l.i == len(l.slice)-1 && l.slice[l.i] == "" { | ||
54 | l.curStart = len(l.orig) | ||
55 | } | ||
56 | } else { | ||
57 | l.curStart = l.curEnd + 1 | ||
58 | if l.curStart == len(l.orig)-1 && l.orig[l.curStart] == '.' { | ||
59 | l.curStart = len(l.orig) | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | |||
64 | func (l *labelIter) set(s string) { | ||
65 | if l.slice == nil { | ||
66 | l.slice = strings.Split(l.orig, ".") | ||
67 | } | ||
68 | l.slice[l.i] = s | ||
69 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/svchost/svchost.go b/vendor/github.com/hashicorp/terraform/svchost/svchost.go new file mode 100644 index 0000000..4eded14 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/svchost/svchost.go | |||
@@ -0,0 +1,207 @@ | |||
1 | // Package svchost deals with the representations of the so-called "friendly | ||
2 | // hostnames" that we use to represent systems that provide Terraform-native | ||
3 | // remote services, such as module registry, remote operations, etc. | ||
4 | // | ||
5 | // Friendly hostnames are specified such that, as much as possible, they | ||
6 | // are consistent with how web browsers think of hostnames, so that users | ||
7 | // can bring their intuitions about how hostnames behave when they access | ||
8 | // a Terraform Enterprise instance's web UI (or indeed any other website) | ||
9 | // and have this behave in a similar way. | ||
10 | package svchost | ||
11 | |||
12 | import ( | ||
13 | "errors" | ||
14 | "fmt" | ||
15 | "strconv" | ||
16 | "strings" | ||
17 | |||
18 | "golang.org/x/net/idna" | ||
19 | ) | ||
20 | |||
21 | // Hostname is specialized name for string that indicates that the string | ||
22 | // has been converted to (or was already in) the storage and comparison form. | ||
23 | // | ||
24 | // Hostname values are not suitable for display in the user-interface. Use | ||
25 | // the ForDisplay method to obtain a form suitable for display in the UI. | ||
26 | // | ||
27 | // Unlike user-supplied hostnames, strings of type Hostname (assuming they | ||
28 | // were constructed by a function within this package) can be compared for | ||
29 | // equality using the standard Go == operator. | ||
30 | type Hostname string | ||
31 | |||
32 | // acePrefix is the ASCII Compatible Encoding prefix, used to indicate that | ||
33 | // a domain name label is in "punycode" form. | ||
34 | const acePrefix = "xn--" | ||
35 | |||
36 | // displayProfile is a very liberal idna profile that we use to do | ||
37 | // normalization for display without imposing validation rules. | ||
38 | var displayProfile = idna.New( | ||
39 | idna.MapForLookup(), | ||
40 | idna.Transitional(true), | ||
41 | ) | ||
42 | |||
43 | // ForDisplay takes a user-specified hostname and returns a normalized form of | ||
44 | // it suitable for display in the UI. | ||
45 | // | ||
46 | // If the input is so invalid that no normalization can be performed then | ||
47 | // this will return the input, assuming that the caller still wants to | ||
48 | // display _something_. This function is, however, more tolerant than the | ||
49 | // other functions in this package and will make a best effort to prepare | ||
50 | // _any_ given hostname for display. | ||
51 | // | ||
52 | // For validation, use either IsValid (for explicit validation) or | ||
53 | // ForComparison (which implicitly validates, returning an error if invalid). | ||
54 | func ForDisplay(given string) string { | ||
55 | var portPortion string | ||
56 | if colonPos := strings.Index(given, ":"); colonPos != -1 { | ||
57 | given, portPortion = given[:colonPos], given[colonPos:] | ||
58 | } | ||
59 | portPortion, _ = normalizePortPortion(portPortion) | ||
60 | |||
61 | ascii, err := displayProfile.ToASCII(given) | ||
62 | if err != nil { | ||
63 | return given + portPortion | ||
64 | } | ||
65 | display, err := displayProfile.ToUnicode(ascii) | ||
66 | if err != nil { | ||
67 | return given + portPortion | ||
68 | } | ||
69 | return display + portPortion | ||
70 | } | ||
71 | |||
72 | // IsValid returns true if the given user-specified hostname is a valid | ||
73 | // service hostname. | ||
74 | // | ||
75 | // Validity is determined by complying with the RFC 5891 requirements for | ||
76 | // names that are valid for domain lookup (section 5), with the additional | ||
77 | // requirement that user-supplied forms must not _already_ contain | ||
78 | // Punycode segments. | ||
79 | func IsValid(given string) bool { | ||
80 | _, err := ForComparison(given) | ||
81 | return err == nil | ||
82 | } | ||
83 | |||
84 | // ForComparison takes a user-specified hostname and returns a normalized | ||
85 | // form of it suitable for storage and comparison. The result is not suitable | ||
86 | // for display to end-users because it uses Punycode to represent non-ASCII | ||
87 | // characters, and this form is unreadable for non-ASCII-speaking humans. | ||
88 | // | ||
89 | // The result is typed as Hostname -- a specialized name for string -- so that | ||
90 | // other APIs can make it clear within the type system whether they expect a | ||
91 | // user-specified or display-form hostname or a value already normalized for | ||
92 | // comparison. | ||
93 | // | ||
94 | // The returned Hostname is not valid if the returned error is non-nil. | ||
95 | func ForComparison(given string) (Hostname, error) { | ||
96 | var portPortion string | ||
97 | if colonPos := strings.Index(given, ":"); colonPos != -1 { | ||
98 | given, portPortion = given[:colonPos], given[colonPos:] | ||
99 | } | ||
100 | |||
101 | var err error | ||
102 | portPortion, err = normalizePortPortion(portPortion) | ||
103 | if err != nil { | ||
104 | return Hostname(""), err | ||
105 | } | ||
106 | |||
107 | if given == "" { | ||
108 | return Hostname(""), fmt.Errorf("empty string is not a valid hostname") | ||
109 | } | ||
110 | |||
111 | // First we'll apply our additional constraint that Punycode must not | ||
112 | // be given directly by the user. This is not an IDN specification | ||
113 | // requirement, but we prohibit it to force users to use human-readable | ||
114 | // hostname forms within Terraform configuration. | ||
115 | labels := labelIter{orig: given} | ||
116 | for ; !labels.done(); labels.next() { | ||
117 | label := labels.label() | ||
118 | if label == "" { | ||
119 | return Hostname(""), fmt.Errorf( | ||
120 | "hostname contains empty label (two consecutive periods)", | ||
121 | ) | ||
122 | } | ||
123 | if strings.HasPrefix(label, acePrefix) { | ||
124 | return Hostname(""), fmt.Errorf( | ||
125 | "hostname label %q specified in punycode format; service hostnames must be given in unicode", | ||
126 | label, | ||
127 | ) | ||
128 | } | ||
129 | } | ||
130 | |||
131 | result, err := idna.Lookup.ToASCII(given) | ||
132 | if err != nil { | ||
133 | return Hostname(""), err | ||
134 | } | ||
135 | return Hostname(result + portPortion), nil | ||
136 | } | ||
137 | |||
138 | // ForDisplay returns a version of the receiver that is appropriate for display | ||
139 | // in the UI. This includes converting any punycode labels to their | ||
140 | // corresponding Unicode characters. | ||
141 | // | ||
142 | // A round-trip through ForComparison and this ForDisplay method does not | ||
143 | // guarantee the same result as calling this package's top-level ForDisplay | ||
144 | // function, since a round-trip through the Hostname type implies stricter | ||
145 | // handling than we do when doing basic display-only processing. | ||
146 | func (h Hostname) ForDisplay() string { | ||
147 | given := string(h) | ||
148 | var portPortion string | ||
149 | if colonPos := strings.Index(given, ":"); colonPos != -1 { | ||
150 | given, portPortion = given[:colonPos], given[colonPos:] | ||
151 | } | ||
152 | // We don't normalize the port portion here because we assume it's | ||
153 | // already been normalized on the way in. | ||
154 | |||
155 | result, err := idna.Lookup.ToUnicode(given) | ||
156 | if err != nil { | ||
157 | // Should never happen, since type Hostname indicates that a string | ||
158 | // passed through our validation rules. | ||
159 | panic(fmt.Errorf("ForDisplay called on invalid Hostname: %s", err)) | ||
160 | } | ||
161 | return result + portPortion | ||
162 | } | ||
163 | |||
164 | func (h Hostname) String() string { | ||
165 | return string(h) | ||
166 | } | ||
167 | |||
168 | func (h Hostname) GoString() string { | ||
169 | return fmt.Sprintf("svchost.Hostname(%q)", string(h)) | ||
170 | } | ||
171 | |||
172 | // normalizePortPortion attempts to normalize the "port portion" of a hostname, | ||
173 | // which begins with the first colon in the hostname and should be followed | ||
174 | // by a string of decimal digits. | ||
175 | // | ||
176 | // If the port portion is valid, a normalized version of it is returned along | ||
177 | // with a nil error. | ||
178 | // | ||
179 | // If the port portion is invalid, the input string is returned verbatim along | ||
180 | // with a non-nil error. | ||
181 | // | ||
182 | // An empty string is a valid port portion representing the absense of a port. | ||
183 | // If non-empty, the first character must be a colon. | ||
184 | func normalizePortPortion(s string) (string, error) { | ||
185 | if s == "" { | ||
186 | return s, nil | ||
187 | } | ||
188 | |||
189 | if s[0] != ':' { | ||
190 | // should never happen, since caller tends to guarantee the presence | ||
191 | // of a colon due to how it's extracted from the string. | ||
192 | return s, errors.New("port portion is missing its initial colon") | ||
193 | } | ||
194 | |||
195 | numStr := s[1:] | ||
196 | num, err := strconv.Atoi(numStr) | ||
197 | if err != nil { | ||
198 | return s, errors.New("port portion contains non-digit characters") | ||
199 | } | ||
200 | if num == 443 { | ||
201 | return "", nil // ":443" is the default | ||
202 | } | ||
203 | if num > 65535 { | ||
204 | return s, errors.New("port number is greater than 65535") | ||
205 | } | ||
206 | return fmt.Sprintf(":%d", num), nil | ||
207 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/context.go b/vendor/github.com/hashicorp/terraform/terraform/context.go index a814a85..f133cc2 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context.go | |||
@@ -8,11 +8,13 @@ import ( | |||
8 | "strings" | 8 | "strings" |
9 | "sync" | 9 | "sync" |
10 | 10 | ||
11 | "github.com/hashicorp/terraform/tfdiags" | ||
12 | |||
11 | "github.com/hashicorp/go-multierror" | 13 | "github.com/hashicorp/go-multierror" |
12 | "github.com/hashicorp/hcl" | 14 | "github.com/hashicorp/hcl" |
13 | "github.com/hashicorp/terraform/config" | 15 | "github.com/hashicorp/terraform/config" |
14 | "github.com/hashicorp/terraform/config/module" | 16 | "github.com/hashicorp/terraform/config/module" |
15 | "github.com/hashicorp/terraform/helper/experiment" | 17 | "github.com/hashicorp/terraform/version" |
16 | ) | 18 | ) |
17 | 19 | ||
18 | // InputMode defines what sort of input will be asked for when Input | 20 | // InputMode defines what sort of input will be asked for when Input |
@@ -123,7 +125,7 @@ type Context struct { | |||
123 | func NewContext(opts *ContextOpts) (*Context, error) { | 125 | func NewContext(opts *ContextOpts) (*Context, error) { |
124 | // Validate the version requirement if it is given | 126 | // Validate the version requirement if it is given |
125 | if opts.Module != nil { | 127 | if opts.Module != nil { |
126 | if err := checkRequiredVersion(opts.Module); err != nil { | 128 | if err := CheckRequiredVersion(opts.Module); err != nil { |
127 | return nil, err | 129 | return nil, err |
128 | } | 130 | } |
129 | } | 131 | } |
@@ -143,19 +145,14 @@ func NewContext(opts *ContextOpts) (*Context, error) { | |||
143 | 145 | ||
144 | // If our state is from the future, then error. Callers can avoid | 146 | // If our state is from the future, then error. Callers can avoid |
145 | // this error by explicitly setting `StateFutureAllowed`. | 147 | // this error by explicitly setting `StateFutureAllowed`. |
146 | if !opts.StateFutureAllowed && state.FromFutureTerraform() { | 148 | if err := CheckStateVersion(state); err != nil && !opts.StateFutureAllowed { |
147 | return nil, fmt.Errorf( | 149 | return nil, err |
148 | "Terraform doesn't allow running any operations against a state\n"+ | ||
149 | "that was written by a future Terraform version. The state is\n"+ | ||
150 | "reporting it is written by Terraform '%s'.\n\n"+ | ||
151 | "Please run at least that version of Terraform to continue.", | ||
152 | state.TFVersion) | ||
153 | } | 150 | } |
154 | 151 | ||
155 | // Explicitly reset our state version to our current version so that | 152 | // Explicitly reset our state version to our current version so that |
156 | // any operations we do will write out that our latest version | 153 | // any operations we do will write out that our latest version |
157 | // has run. | 154 | // has run. |
158 | state.TFVersion = Version | 155 | state.TFVersion = version.Version |
159 | 156 | ||
160 | // Determine parallelism, default to 10. We do this both to limit | 157 | // Determine parallelism, default to 10. We do this both to limit |
161 | // CPU pressure but also to have an extra guard against rate throttling | 158 | // CPU pressure but also to have an extra guard against rate throttling |
@@ -465,7 +462,7 @@ func (c *Context) Input(mode InputMode) error { | |||
465 | } | 462 | } |
466 | 463 | ||
467 | // Do the walk | 464 | // Do the walk |
468 | if _, err := c.walk(graph, nil, walkInput); err != nil { | 465 | if _, err := c.walk(graph, walkInput); err != nil { |
469 | return err | 466 | return err |
470 | } | 467 | } |
471 | } | 468 | } |
@@ -490,6 +487,13 @@ func (c *Context) Input(mode InputMode) error { | |||
490 | func (c *Context) Apply() (*State, error) { | 487 | func (c *Context) Apply() (*State, error) { |
491 | defer c.acquireRun("apply")() | 488 | defer c.acquireRun("apply")() |
492 | 489 | ||
490 | // Check there are no empty target parameter values | ||
491 | for _, target := range c.targets { | ||
492 | if target == "" { | ||
493 | return nil, fmt.Errorf("Target parameter must not have empty value") | ||
494 | } | ||
495 | } | ||
496 | |||
493 | // Copy our own state | 497 | // Copy our own state |
494 | c.state = c.state.DeepCopy() | 498 | c.state = c.state.DeepCopy() |
495 | 499 | ||
@@ -506,7 +510,7 @@ func (c *Context) Apply() (*State, error) { | |||
506 | } | 510 | } |
507 | 511 | ||
508 | // Walk the graph | 512 | // Walk the graph |
509 | walker, err := c.walk(graph, graph, operation) | 513 | walker, err := c.walk(graph, operation) |
510 | if len(walker.ValidationErrors) > 0 { | 514 | if len(walker.ValidationErrors) > 0 { |
511 | err = multierror.Append(err, walker.ValidationErrors...) | 515 | err = multierror.Append(err, walker.ValidationErrors...) |
512 | } | 516 | } |
@@ -527,19 +531,27 @@ func (c *Context) Apply() (*State, error) { | |||
527 | func (c *Context) Plan() (*Plan, error) { | 531 | func (c *Context) Plan() (*Plan, error) { |
528 | defer c.acquireRun("plan")() | 532 | defer c.acquireRun("plan")() |
529 | 533 | ||
534 | // Check there are no empty target parameter values | ||
535 | for _, target := range c.targets { | ||
536 | if target == "" { | ||
537 | return nil, fmt.Errorf("Target parameter must not have empty value") | ||
538 | } | ||
539 | } | ||
540 | |||
530 | p := &Plan{ | 541 | p := &Plan{ |
531 | Module: c.module, | 542 | Module: c.module, |
532 | Vars: c.variables, | 543 | Vars: c.variables, |
533 | State: c.state, | 544 | State: c.state, |
534 | Targets: c.targets, | 545 | Targets: c.targets, |
535 | 546 | ||
536 | TerraformVersion: VersionString(), | 547 | TerraformVersion: version.String(), |
537 | ProviderSHA256s: c.providerSHA256s, | 548 | ProviderSHA256s: c.providerSHA256s, |
538 | } | 549 | } |
539 | 550 | ||
540 | var operation walkOperation | 551 | var operation walkOperation |
541 | if c.destroy { | 552 | if c.destroy { |
542 | operation = walkPlanDestroy | 553 | operation = walkPlanDestroy |
554 | p.Destroy = true | ||
543 | } else { | 555 | } else { |
544 | // Set our state to be something temporary. We do this so that | 556 | // Set our state to be something temporary. We do this so that |
545 | // the plan can update a fake state so that variables work, then | 557 | // the plan can update a fake state so that variables work, then |
@@ -575,7 +587,7 @@ func (c *Context) Plan() (*Plan, error) { | |||
575 | } | 587 | } |
576 | 588 | ||
577 | // Do the walk | 589 | // Do the walk |
578 | walker, err := c.walk(graph, graph, operation) | 590 | walker, err := c.walk(graph, operation) |
579 | if err != nil { | 591 | if err != nil { |
580 | return nil, err | 592 | return nil, err |
581 | } | 593 | } |
@@ -630,7 +642,7 @@ func (c *Context) Refresh() (*State, error) { | |||
630 | } | 642 | } |
631 | 643 | ||
632 | // Do the walk | 644 | // Do the walk |
633 | if _, err := c.walk(graph, graph, walkRefresh); err != nil { | 645 | if _, err := c.walk(graph, walkRefresh); err != nil { |
634 | return nil, err | 646 | return nil, err |
635 | } | 647 | } |
636 | 648 | ||
@@ -670,29 +682,27 @@ func (c *Context) Stop() { | |||
670 | } | 682 | } |
671 | 683 | ||
672 | // Validate validates the configuration and returns any warnings or errors. | 684 | // Validate validates the configuration and returns any warnings or errors. |
673 | func (c *Context) Validate() ([]string, []error) { | 685 | func (c *Context) Validate() tfdiags.Diagnostics { |
674 | defer c.acquireRun("validate")() | 686 | defer c.acquireRun("validate")() |
675 | 687 | ||
676 | var errs error | 688 | var diags tfdiags.Diagnostics |
677 | 689 | ||
678 | // Validate the configuration itself | 690 | // Validate the configuration itself |
679 | if err := c.module.Validate(); err != nil { | 691 | diags = diags.Append(c.module.Validate()) |
680 | errs = multierror.Append(errs, err) | ||
681 | } | ||
682 | 692 | ||
683 | // This only needs to be done for the root module, since inter-module | 693 | // This only needs to be done for the root module, since inter-module |
684 | // variables are validated in the module tree. | 694 | // variables are validated in the module tree. |
685 | if config := c.module.Config(); config != nil { | 695 | if config := c.module.Config(); config != nil { |
686 | // Validate the user variables | 696 | // Validate the user variables |
687 | if err := smcUserVariables(config, c.variables); len(err) > 0 { | 697 | for _, err := range smcUserVariables(config, c.variables) { |
688 | errs = multierror.Append(errs, err...) | 698 | diags = diags.Append(err) |
689 | } | 699 | } |
690 | } | 700 | } |
691 | 701 | ||
692 | // If we have errors at this point, the graphing has no chance, | 702 | // If we have errors at this point, the graphing has no chance, |
693 | // so just bail early. | 703 | // so just bail early. |
694 | if errs != nil { | 704 | if diags.HasErrors() { |
695 | return nil, []error{errs} | 705 | return diags |
696 | } | 706 | } |
697 | 707 | ||
698 | // Build the graph so we can walk it and run Validate on nodes. | 708 | // Build the graph so we can walk it and run Validate on nodes. |
@@ -701,24 +711,29 @@ func (c *Context) Validate() ([]string, []error) { | |||
701 | // graph again later after Planning. | 711 | // graph again later after Planning. |
702 | graph, err := c.Graph(GraphTypeValidate, nil) | 712 | graph, err := c.Graph(GraphTypeValidate, nil) |
703 | if err != nil { | 713 | if err != nil { |
704 | return nil, []error{err} | 714 | diags = diags.Append(err) |
715 | return diags | ||
705 | } | 716 | } |
706 | 717 | ||
707 | // Walk | 718 | // Walk |
708 | walker, err := c.walk(graph, graph, walkValidate) | 719 | walker, err := c.walk(graph, walkValidate) |
709 | if err != nil { | 720 | if err != nil { |
710 | return nil, multierror.Append(errs, err).Errors | 721 | diags = diags.Append(err) |
711 | } | 722 | } |
712 | 723 | ||
713 | // Return the result | ||
714 | rerrs := multierror.Append(errs, walker.ValidationErrors...) | ||
715 | |||
716 | sort.Strings(walker.ValidationWarnings) | 724 | sort.Strings(walker.ValidationWarnings) |
717 | sort.Slice(rerrs.Errors, func(i, j int) bool { | 725 | sort.Slice(walker.ValidationErrors, func(i, j int) bool { |
718 | return rerrs.Errors[i].Error() < rerrs.Errors[j].Error() | 726 | return walker.ValidationErrors[i].Error() < walker.ValidationErrors[j].Error() |
719 | }) | 727 | }) |
720 | 728 | ||
721 | return walker.ValidationWarnings, rerrs.Errors | 729 | for _, warn := range walker.ValidationWarnings { |
730 | diags = diags.Append(tfdiags.SimpleWarning(warn)) | ||
731 | } | ||
732 | for _, err := range walker.ValidationErrors { | ||
733 | diags = diags.Append(err) | ||
734 | } | ||
735 | |||
736 | return diags | ||
722 | } | 737 | } |
723 | 738 | ||
724 | // Module returns the module tree associated with this context. | 739 | // Module returns the module tree associated with this context. |
@@ -792,33 +807,11 @@ func (c *Context) releaseRun() { | |||
792 | c.runContext = nil | 807 | c.runContext = nil |
793 | } | 808 | } |
794 | 809 | ||
795 | func (c *Context) walk( | 810 | func (c *Context) walk(graph *Graph, operation walkOperation) (*ContextGraphWalker, error) { |
796 | graph, shadow *Graph, operation walkOperation) (*ContextGraphWalker, error) { | ||
797 | // Keep track of the "real" context which is the context that does | 811 | // Keep track of the "real" context which is the context that does |
798 | // the real work: talking to real providers, modifying real state, etc. | 812 | // the real work: talking to real providers, modifying real state, etc. |
799 | realCtx := c | 813 | realCtx := c |
800 | 814 | ||
801 | // If we don't want shadowing, remove it | ||
802 | if !experiment.Enabled(experiment.X_shadow) { | ||
803 | shadow = nil | ||
804 | } | ||
805 | |||
806 | // Just log this so we can see it in a debug log | ||
807 | if !c.shadow { | ||
808 | log.Printf("[WARN] terraform: shadow graph disabled") | ||
809 | shadow = nil | ||
810 | } | ||
811 | |||
812 | // If we have a shadow graph, walk that as well | ||
813 | var shadowCtx *Context | ||
814 | var shadowCloser Shadow | ||
815 | if shadow != nil { | ||
816 | // Build the shadow context. In the process, override the real context | ||
817 | // with the one that is wrapped so that the shadow context can verify | ||
818 | // the results of the real. | ||
819 | realCtx, shadowCtx, shadowCloser = newShadowContext(c) | ||
820 | } | ||
821 | |||
822 | log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) | 815 | log.Printf("[DEBUG] Starting graph walk: %s", operation.String()) |
823 | 816 | ||
824 | walker := &ContextGraphWalker{ | 817 | walker := &ContextGraphWalker{ |
@@ -837,90 +830,6 @@ func (c *Context) walk( | |||
837 | close(watchStop) | 830 | close(watchStop) |
838 | <-watchWait | 831 | <-watchWait |
839 | 832 | ||
840 | // If we have a shadow graph and we interrupted the real graph, then | ||
841 | // we just close the shadow and never verify it. It is non-trivial to | ||
842 | // recreate the exact execution state up until an interruption so this | ||
843 | // isn't supported with shadows at the moment. | ||
844 | if shadowCloser != nil && c.sh.Stopped() { | ||
845 | // Ignore the error result, there is nothing we could care about | ||
846 | shadowCloser.CloseShadow() | ||
847 | |||
848 | // Set it to nil so we don't do anything | ||
849 | shadowCloser = nil | ||
850 | } | ||
851 | |||
852 | // If we have a shadow graph, wait for that to complete. | ||
853 | if shadowCloser != nil { | ||
854 | // Build the graph walker for the shadow. We also wrap this in | ||
855 | // a panicwrap so that panics are captured. For the shadow graph, | ||
856 | // we just want panics to be normal errors rather than to crash | ||
857 | // Terraform. | ||
858 | shadowWalker := GraphWalkerPanicwrap(&ContextGraphWalker{ | ||
859 | Context: shadowCtx, | ||
860 | Operation: operation, | ||
861 | }) | ||
862 | |||
863 | // Kick off the shadow walk. This will block on any operations | ||
864 | // on the real walk so it is fine to start first. | ||
865 | log.Printf("[INFO] Starting shadow graph walk: %s", operation.String()) | ||
866 | shadowCh := make(chan error) | ||
867 | go func() { | ||
868 | shadowCh <- shadow.Walk(shadowWalker) | ||
869 | }() | ||
870 | |||
871 | // Notify the shadow that we're done | ||
872 | if err := shadowCloser.CloseShadow(); err != nil { | ||
873 | c.shadowErr = multierror.Append(c.shadowErr, err) | ||
874 | } | ||
875 | |||
876 | // Wait for the walk to end | ||
877 | log.Printf("[DEBUG] Waiting for shadow graph to complete...") | ||
878 | shadowWalkErr := <-shadowCh | ||
879 | |||
880 | // Get any shadow errors | ||
881 | if err := shadowCloser.ShadowError(); err != nil { | ||
882 | c.shadowErr = multierror.Append(c.shadowErr, err) | ||
883 | } | ||
884 | |||
885 | // Verify the contexts (compare) | ||
886 | if err := shadowContextVerify(realCtx, shadowCtx); err != nil { | ||
887 | c.shadowErr = multierror.Append(c.shadowErr, err) | ||
888 | } | ||
889 | |||
890 | // At this point, if we're supposed to fail on error, then | ||
891 | // we PANIC. Some tests just verify that there is an error, | ||
892 | // so simply appending it to realErr and returning could hide | ||
893 | // shadow problems. | ||
894 | // | ||
895 | // This must be done BEFORE appending shadowWalkErr since the | ||
896 | // shadowWalkErr may include expected errors. | ||
897 | // | ||
898 | // We only do this if we don't have a real error. In the case of | ||
899 | // a real error, we can't guarantee what nodes were and weren't | ||
900 | // traversed in parallel scenarios so we can't guarantee no | ||
901 | // shadow errors. | ||
902 | if c.shadowErr != nil && contextFailOnShadowError && realErr == nil { | ||
903 | panic(multierror.Prefix(c.shadowErr, "shadow graph:")) | ||
904 | } | ||
905 | |||
906 | // Now, if we have a walk error, we append that through | ||
907 | if shadowWalkErr != nil { | ||
908 | c.shadowErr = multierror.Append(c.shadowErr, shadowWalkErr) | ||
909 | } | ||
910 | |||
911 | if c.shadowErr == nil { | ||
912 | log.Printf("[INFO] Shadow graph success!") | ||
913 | } else { | ||
914 | log.Printf("[ERROR] Shadow graph error: %s", c.shadowErr) | ||
915 | |||
916 | // If we're supposed to fail on shadow errors, then report it | ||
917 | if contextFailOnShadowError { | ||
918 | realErr = multierror.Append(realErr, multierror.Prefix( | ||
919 | c.shadowErr, "shadow graph:")) | ||
920 | } | ||
921 | } | ||
922 | } | ||
923 | |||
924 | return walker, realErr | 833 | return walker, realErr |
925 | } | 834 | } |
926 | 835 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/context_import.go b/vendor/github.com/hashicorp/terraform/terraform/context_import.go index f1d5776..e940143 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/context_import.go +++ b/vendor/github.com/hashicorp/terraform/terraform/context_import.go | |||
@@ -66,7 +66,7 @@ func (c *Context) Import(opts *ImportOpts) (*State, error) { | |||
66 | } | 66 | } |
67 | 67 | ||
68 | // Walk it | 68 | // Walk it |
69 | if _, err := c.walk(graph, nil, walkImport); err != nil { | 69 | if _, err := c.walk(graph, walkImport); err != nil { |
70 | return c.state, err | 70 | return c.state, err |
71 | } | 71 | } |
72 | 72 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/diff.go b/vendor/github.com/hashicorp/terraform/terraform/diff.go index fd1687e..d6dc550 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/diff.go +++ b/vendor/github.com/hashicorp/terraform/terraform/diff.go | |||
@@ -23,6 +23,12 @@ const ( | |||
23 | DiffUpdate | 23 | DiffUpdate |
24 | DiffDestroy | 24 | DiffDestroy |
25 | DiffDestroyCreate | 25 | DiffDestroyCreate |
26 | |||
27 | // DiffRefresh is only used in the UI for displaying diffs. | ||
28 | // Managed resource reads never appear in plan, and when data source | ||
29 | // reads appear they are represented as DiffCreate in core before | ||
30 | // transforming to DiffRefresh in the UI layer. | ||
31 | DiffRefresh // TODO: Actually use DiffRefresh in core too, for less confusion | ||
26 | ) | 32 | ) |
27 | 33 | ||
28 | // multiVal matches the index key to a flatmapped set, list or map | 34 | // multiVal matches the index key to a flatmapped set, list or map |
@@ -831,7 +837,14 @@ func (d *InstanceDiff) Same(d2 *InstanceDiff) (bool, string) { | |||
831 | } | 837 | } |
832 | } | 838 | } |
833 | 839 | ||
834 | // TODO: check for the same value if not computed | 840 | // We don't compare the values because we can't currently actually |
841 | // guarantee to generate the same value two two diffs created from | ||
842 | // the same state+config: we have some pesky interpolation functions | ||
843 | // that do not behave as pure functions (uuid, timestamp) and so they | ||
844 | // can be different each time a diff is produced. | ||
845 | // FIXME: Re-organize our config handling so that we don't re-evaluate | ||
846 | // expressions when we produce a second comparison diff during | ||
847 | // apply (for EvalCompareDiff). | ||
835 | } | 848 | } |
836 | 849 | ||
837 | // Check for leftover attributes | 850 | // Check for leftover attributes |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval.go b/vendor/github.com/hashicorp/terraform/terraform/eval.go index 3cb088a..10d9c22 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval.go | |||
@@ -49,11 +49,11 @@ func EvalRaw(n EvalNode, ctx EvalContext) (interface{}, error) { | |||
49 | path = strings.Join(ctx.Path(), ".") | 49 | path = strings.Join(ctx.Path(), ".") |
50 | } | 50 | } |
51 | 51 | ||
52 | log.Printf("[DEBUG] %s: eval: %T", path, n) | 52 | log.Printf("[TRACE] %s: eval: %T", path, n) |
53 | output, err := n.Eval(ctx) | 53 | output, err := n.Eval(ctx) |
54 | if err != nil { | 54 | if err != nil { |
55 | if _, ok := err.(EvalEarlyExitError); ok { | 55 | if _, ok := err.(EvalEarlyExitError); ok { |
56 | log.Printf("[DEBUG] %s: eval: %T, err: %s", path, n, err) | 56 | log.Printf("[TRACE] %s: eval: %T, err: %s", path, n, err) |
57 | } else { | 57 | } else { |
58 | log.Printf("[ERROR] %s: eval: %T, err: %s", path, n, err) | 58 | log.Printf("[ERROR] %s: eval: %T, err: %s", path, n, err) |
59 | } | 59 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go b/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go index 2f6a497..b9b4806 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_apply.go | |||
@@ -112,7 +112,7 @@ func (n *EvalApplyPre) Eval(ctx EvalContext) (interface{}, error) { | |||
112 | } | 112 | } |
113 | state.init() | 113 | state.init() |
114 | 114 | ||
115 | { | 115 | if resourceHasUserVisibleApply(n.Info) { |
116 | // Call post-apply hook | 116 | // Call post-apply hook |
117 | err := ctx.Hook(func(h Hook) (HookAction, error) { | 117 | err := ctx.Hook(func(h Hook) (HookAction, error) { |
118 | return h.PreApply(n.Info, state, diff) | 118 | return h.PreApply(n.Info, state, diff) |
@@ -136,7 +136,7 @@ type EvalApplyPost struct { | |||
136 | func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) { | 136 | func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) { |
137 | state := *n.State | 137 | state := *n.State |
138 | 138 | ||
139 | { | 139 | if resourceHasUserVisibleApply(n.Info) { |
140 | // Call post-apply hook | 140 | // Call post-apply hook |
141 | err := ctx.Hook(func(h Hook) (HookAction, error) { | 141 | err := ctx.Hook(func(h Hook) (HookAction, error) { |
142 | return h.PostApply(n.Info, state, *n.Error) | 142 | return h.PostApply(n.Info, state, *n.Error) |
@@ -149,6 +149,22 @@ func (n *EvalApplyPost) Eval(ctx EvalContext) (interface{}, error) { | |||
149 | return nil, *n.Error | 149 | return nil, *n.Error |
150 | } | 150 | } |
151 | 151 | ||
152 | // resourceHasUserVisibleApply returns true if the given resource is one where | ||
153 | // apply actions should be exposed to the user. | ||
154 | // | ||
155 | // Certain resources do apply actions only as an implementation detail, so | ||
156 | // these should not be advertised to code outside of this package. | ||
157 | func resourceHasUserVisibleApply(info *InstanceInfo) bool { | ||
158 | addr := info.ResourceAddress() | ||
159 | |||
160 | // Only managed resources have user-visible apply actions. | ||
161 | // In particular, this excludes data resources since we "apply" these | ||
162 | // only as an implementation detail of removing them from state when | ||
163 | // they are destroyed. (When reading, they don't get here at all because | ||
164 | // we present them as "Refresh" actions.) | ||
165 | return addr.Mode == config.ManagedResourceMode | ||
166 | } | ||
167 | |||
152 | // EvalApplyProvisioners is an EvalNode implementation that executes | 168 | // EvalApplyProvisioners is an EvalNode implementation that executes |
153 | // the provisioners for a resource. | 169 | // the provisioners for a resource. |
154 | // | 170 | // |
@@ -211,11 +227,8 @@ func (n *EvalApplyProvisioners) Eval(ctx EvalContext) (interface{}, error) { | |||
211 | state.Tainted = true | 227 | state.Tainted = true |
212 | } | 228 | } |
213 | 229 | ||
214 | if n.Error != nil { | 230 | *n.Error = multierror.Append(*n.Error, err) |
215 | *n.Error = multierror.Append(*n.Error, err) | 231 | return nil, err |
216 | } else { | ||
217 | return nil, err | ||
218 | } | ||
219 | } | 232 | } |
220 | 233 | ||
221 | { | 234 | { |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_context.go b/vendor/github.com/hashicorp/terraform/terraform/eval_context.go index a1f815b..86481de 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_context.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_context.go | |||
@@ -22,11 +22,11 @@ type EvalContext interface { | |||
22 | // Input is the UIInput object for interacting with the UI. | 22 | // Input is the UIInput object for interacting with the UI. |
23 | Input() UIInput | 23 | Input() UIInput |
24 | 24 | ||
25 | // InitProvider initializes the provider with the given name and | 25 | // InitProvider initializes the provider with the given type and name, and |
26 | // returns the implementation of the resource provider or an error. | 26 | // returns the implementation of the resource provider or an error. |
27 | // | 27 | // |
28 | // It is an error to initialize the same provider more than once. | 28 | // It is an error to initialize the same provider more than once. |
29 | InitProvider(string) (ResourceProvider, error) | 29 | InitProvider(typ string, name string) (ResourceProvider, error) |
30 | 30 | ||
31 | // Provider gets the provider instance with the given name (already | 31 | // Provider gets the provider instance with the given name (already |
32 | // initialized) or returns nil if the provider isn't initialized. | 32 | // initialized) or returns nil if the provider isn't initialized. |
@@ -40,8 +40,6 @@ type EvalContext interface { | |||
40 | // is used to store the provider configuration for inheritance lookups | 40 | // is used to store the provider configuration for inheritance lookups |
41 | // with ParentProviderConfig(). | 41 | // with ParentProviderConfig(). |
42 | ConfigureProvider(string, *ResourceConfig) error | 42 | ConfigureProvider(string, *ResourceConfig) error |
43 | SetProviderConfig(string, *ResourceConfig) error | ||
44 | ParentProviderConfig(string) *ResourceConfig | ||
45 | 43 | ||
46 | // ProviderInput and SetProviderInput are used to configure providers | 44 | // ProviderInput and SetProviderInput are used to configure providers |
47 | // from user input. | 45 | // from user input. |
@@ -69,6 +67,13 @@ type EvalContext interface { | |||
69 | // that is currently being acted upon. | 67 | // that is currently being acted upon. |
70 | Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error) | 68 | Interpolate(*config.RawConfig, *Resource) (*ResourceConfig, error) |
71 | 69 | ||
70 | // InterpolateProvider takes a ProviderConfig and interpolates it with the | ||
71 | // stored interpolation scope. Since provider configurations can be | ||
72 | // inherited, the interpolation scope may be different from the current | ||
73 | // context path. Interplation is otherwise executed the same as in the | ||
74 | // Interpolation method. | ||
75 | InterpolateProvider(*config.ProviderConfig, *Resource) (*ResourceConfig, error) | ||
76 | |||
72 | // SetVariables sets the variables for the module within | 77 | // SetVariables sets the variables for the module within |
73 | // this context with the name n. This function call is additive: | 78 | // this context with the name n. This function call is additive: |
74 | // the second parameter is merged with any previous call. | 79 | // the second parameter is merged with any previous call. |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go b/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go index 3dcfb22..1b6ee5a 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_context_builtin.go | |||
@@ -4,7 +4,6 @@ import ( | |||
4 | "context" | 4 | "context" |
5 | "fmt" | 5 | "fmt" |
6 | "log" | 6 | "log" |
7 | "strings" | ||
8 | "sync" | 7 | "sync" |
9 | 8 | ||
10 | "github.com/hashicorp/terraform/config" | 9 | "github.com/hashicorp/terraform/config" |
@@ -34,7 +33,6 @@ type BuiltinEvalContext struct { | |||
34 | Hooks []Hook | 33 | Hooks []Hook |
35 | InputValue UIInput | 34 | InputValue UIInput |
36 | ProviderCache map[string]ResourceProvider | 35 | ProviderCache map[string]ResourceProvider |
37 | ProviderConfigCache map[string]*ResourceConfig | ||
38 | ProviderInputConfig map[string]map[string]interface{} | 36 | ProviderInputConfig map[string]map[string]interface{} |
39 | ProviderLock *sync.Mutex | 37 | ProviderLock *sync.Mutex |
40 | ProvisionerCache map[string]ResourceProvisioner | 38 | ProvisionerCache map[string]ResourceProvisioner |
@@ -80,12 +78,12 @@ func (ctx *BuiltinEvalContext) Input() UIInput { | |||
80 | return ctx.InputValue | 78 | return ctx.InputValue |
81 | } | 79 | } |
82 | 80 | ||
83 | func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) { | 81 | func (ctx *BuiltinEvalContext) InitProvider(typeName, name string) (ResourceProvider, error) { |
84 | ctx.once.Do(ctx.init) | 82 | ctx.once.Do(ctx.init) |
85 | 83 | ||
86 | // If we already initialized, it is an error | 84 | // If we already initialized, it is an error |
87 | if p := ctx.Provider(n); p != nil { | 85 | if p := ctx.Provider(name); p != nil { |
88 | return nil, fmt.Errorf("Provider '%s' already initialized", n) | 86 | return nil, fmt.Errorf("Provider '%s' already initialized", name) |
89 | } | 87 | } |
90 | 88 | ||
91 | // Warning: make sure to acquire these locks AFTER the call to Provider | 89 | // Warning: make sure to acquire these locks AFTER the call to Provider |
@@ -93,18 +91,12 @@ func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) | |||
93 | ctx.ProviderLock.Lock() | 91 | ctx.ProviderLock.Lock() |
94 | defer ctx.ProviderLock.Unlock() | 92 | defer ctx.ProviderLock.Unlock() |
95 | 93 | ||
96 | providerPath := make([]string, len(ctx.Path())+1) | 94 | p, err := ctx.Components.ResourceProvider(typeName, name) |
97 | copy(providerPath, ctx.Path()) | ||
98 | providerPath[len(providerPath)-1] = n | ||
99 | key := PathCacheKey(providerPath) | ||
100 | |||
101 | typeName := strings.SplitN(n, ".", 2)[0] | ||
102 | p, err := ctx.Components.ResourceProvider(typeName, key) | ||
103 | if err != nil { | 95 | if err != nil { |
104 | return nil, err | 96 | return nil, err |
105 | } | 97 | } |
106 | 98 | ||
107 | ctx.ProviderCache[key] = p | 99 | ctx.ProviderCache[name] = p |
108 | return p, nil | 100 | return p, nil |
109 | } | 101 | } |
110 | 102 | ||
@@ -114,11 +106,7 @@ func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider { | |||
114 | ctx.ProviderLock.Lock() | 106 | ctx.ProviderLock.Lock() |
115 | defer ctx.ProviderLock.Unlock() | 107 | defer ctx.ProviderLock.Unlock() |
116 | 108 | ||
117 | providerPath := make([]string, len(ctx.Path())+1) | 109 | return ctx.ProviderCache[n] |
118 | copy(providerPath, ctx.Path()) | ||
119 | providerPath[len(providerPath)-1] = n | ||
120 | |||
121 | return ctx.ProviderCache[PathCacheKey(providerPath)] | ||
122 | } | 110 | } |
123 | 111 | ||
124 | func (ctx *BuiltinEvalContext) CloseProvider(n string) error { | 112 | func (ctx *BuiltinEvalContext) CloseProvider(n string) error { |
@@ -127,15 +115,11 @@ func (ctx *BuiltinEvalContext) CloseProvider(n string) error { | |||
127 | ctx.ProviderLock.Lock() | 115 | ctx.ProviderLock.Lock() |
128 | defer ctx.ProviderLock.Unlock() | 116 | defer ctx.ProviderLock.Unlock() |
129 | 117 | ||
130 | providerPath := make([]string, len(ctx.Path())+1) | ||
131 | copy(providerPath, ctx.Path()) | ||
132 | providerPath[len(providerPath)-1] = n | ||
133 | |||
134 | var provider interface{} | 118 | var provider interface{} |
135 | provider = ctx.ProviderCache[PathCacheKey(providerPath)] | 119 | provider = ctx.ProviderCache[n] |
136 | if provider != nil { | 120 | if provider != nil { |
137 | if p, ok := provider.(ResourceProviderCloser); ok { | 121 | if p, ok := provider.(ResourceProviderCloser); ok { |
138 | delete(ctx.ProviderCache, PathCacheKey(providerPath)) | 122 | delete(ctx.ProviderCache, n) |
139 | return p.Close() | 123 | return p.Close() |
140 | } | 124 | } |
141 | } | 125 | } |
@@ -149,28 +133,9 @@ func (ctx *BuiltinEvalContext) ConfigureProvider( | |||
149 | if p == nil { | 133 | if p == nil { |
150 | return fmt.Errorf("Provider '%s' not initialized", n) | 134 | return fmt.Errorf("Provider '%s' not initialized", n) |
151 | } | 135 | } |
152 | |||
153 | if err := ctx.SetProviderConfig(n, cfg); err != nil { | ||
154 | return nil | ||
155 | } | ||
156 | |||
157 | return p.Configure(cfg) | 136 | return p.Configure(cfg) |
158 | } | 137 | } |
159 | 138 | ||
160 | func (ctx *BuiltinEvalContext) SetProviderConfig( | ||
161 | n string, cfg *ResourceConfig) error { | ||
162 | providerPath := make([]string, len(ctx.Path())+1) | ||
163 | copy(providerPath, ctx.Path()) | ||
164 | providerPath[len(providerPath)-1] = n | ||
165 | |||
166 | // Save the configuration | ||
167 | ctx.ProviderLock.Lock() | ||
168 | ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg | ||
169 | ctx.ProviderLock.Unlock() | ||
170 | |||
171 | return nil | ||
172 | } | ||
173 | |||
174 | func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} { | 139 | func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} { |
175 | ctx.ProviderLock.Lock() | 140 | ctx.ProviderLock.Lock() |
176 | defer ctx.ProviderLock.Unlock() | 141 | defer ctx.ProviderLock.Unlock() |
@@ -203,27 +168,6 @@ func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface | |||
203 | ctx.ProviderLock.Unlock() | 168 | ctx.ProviderLock.Unlock() |
204 | } | 169 | } |
205 | 170 | ||
206 | func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig { | ||
207 | ctx.ProviderLock.Lock() | ||
208 | defer ctx.ProviderLock.Unlock() | ||
209 | |||
210 | // Make a copy of the path so we can safely edit it | ||
211 | path := ctx.Path() | ||
212 | pathCopy := make([]string, len(path)+1) | ||
213 | copy(pathCopy, path) | ||
214 | |||
215 | // Go up the tree. | ||
216 | for i := len(path) - 1; i >= 0; i-- { | ||
217 | pathCopy[i+1] = n | ||
218 | k := PathCacheKey(pathCopy[:i+2]) | ||
219 | if v, ok := ctx.ProviderConfigCache[k]; ok { | ||
220 | return v | ||
221 | } | ||
222 | } | ||
223 | |||
224 | return nil | ||
225 | } | ||
226 | |||
227 | func (ctx *BuiltinEvalContext) InitProvisioner( | 171 | func (ctx *BuiltinEvalContext) InitProvisioner( |
228 | n string) (ResourceProvisioner, error) { | 172 | n string) (ResourceProvisioner, error) { |
229 | ctx.once.Do(ctx.init) | 173 | ctx.once.Do(ctx.init) |
@@ -289,6 +233,7 @@ func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error { | |||
289 | 233 | ||
290 | func (ctx *BuiltinEvalContext) Interpolate( | 234 | func (ctx *BuiltinEvalContext) Interpolate( |
291 | cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) { | 235 | cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) { |
236 | |||
292 | if cfg != nil { | 237 | if cfg != nil { |
293 | scope := &InterpolationScope{ | 238 | scope := &InterpolationScope{ |
294 | Path: ctx.Path(), | 239 | Path: ctx.Path(), |
@@ -311,6 +256,35 @@ func (ctx *BuiltinEvalContext) Interpolate( | |||
311 | return result, nil | 256 | return result, nil |
312 | } | 257 | } |
313 | 258 | ||
259 | func (ctx *BuiltinEvalContext) InterpolateProvider( | ||
260 | pc *config.ProviderConfig, r *Resource) (*ResourceConfig, error) { | ||
261 | |||
262 | var cfg *config.RawConfig | ||
263 | |||
264 | if pc != nil && pc.RawConfig != nil { | ||
265 | scope := &InterpolationScope{ | ||
266 | Path: ctx.Path(), | ||
267 | Resource: r, | ||
268 | } | ||
269 | |||
270 | cfg = pc.RawConfig | ||
271 | |||
272 | vs, err := ctx.Interpolater.Values(scope, cfg.Variables) | ||
273 | if err != nil { | ||
274 | return nil, err | ||
275 | } | ||
276 | |||
277 | // Do the interpolation | ||
278 | if err := cfg.Interpolate(vs); err != nil { | ||
279 | return nil, err | ||
280 | } | ||
281 | } | ||
282 | |||
283 | result := NewResourceConfig(cfg) | ||
284 | result.interpolateForce() | ||
285 | return result, nil | ||
286 | } | ||
287 | |||
314 | func (ctx *BuiltinEvalContext) Path() []string { | 288 | func (ctx *BuiltinEvalContext) Path() []string { |
315 | return ctx.PathValue | 289 | return ctx.PathValue |
316 | } | 290 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go b/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go index 4f90d5b..6464517 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_context_mock.go | |||
@@ -45,14 +45,6 @@ type MockEvalContext struct { | |||
45 | ConfigureProviderConfig *ResourceConfig | 45 | ConfigureProviderConfig *ResourceConfig |
46 | ConfigureProviderError error | 46 | ConfigureProviderError error |
47 | 47 | ||
48 | SetProviderConfigCalled bool | ||
49 | SetProviderConfigName string | ||
50 | SetProviderConfigConfig *ResourceConfig | ||
51 | |||
52 | ParentProviderConfigCalled bool | ||
53 | ParentProviderConfigName string | ||
54 | ParentProviderConfigConfig *ResourceConfig | ||
55 | |||
56 | InitProvisionerCalled bool | 48 | InitProvisionerCalled bool |
57 | InitProvisionerName string | 49 | InitProvisionerName string |
58 | InitProvisionerProvisioner ResourceProvisioner | 50 | InitProvisionerProvisioner ResourceProvisioner |
@@ -72,6 +64,12 @@ type MockEvalContext struct { | |||
72 | InterpolateConfigResult *ResourceConfig | 64 | InterpolateConfigResult *ResourceConfig |
73 | InterpolateError error | 65 | InterpolateError error |
74 | 66 | ||
67 | InterpolateProviderCalled bool | ||
68 | InterpolateProviderConfig *config.ProviderConfig | ||
69 | InterpolateProviderResource *Resource | ||
70 | InterpolateProviderConfigResult *ResourceConfig | ||
71 | InterpolateProviderError error | ||
72 | |||
75 | PathCalled bool | 73 | PathCalled bool |
76 | PathPath []string | 74 | PathPath []string |
77 | 75 | ||
@@ -109,7 +107,7 @@ func (c *MockEvalContext) Input() UIInput { | |||
109 | return c.InputInput | 107 | return c.InputInput |
110 | } | 108 | } |
111 | 109 | ||
112 | func (c *MockEvalContext) InitProvider(n string) (ResourceProvider, error) { | 110 | func (c *MockEvalContext) InitProvider(t, n string) (ResourceProvider, error) { |
113 | c.InitProviderCalled = true | 111 | c.InitProviderCalled = true |
114 | c.InitProviderName = n | 112 | c.InitProviderName = n |
115 | return c.InitProviderProvider, c.InitProviderError | 113 | return c.InitProviderProvider, c.InitProviderError |
@@ -134,20 +132,6 @@ func (c *MockEvalContext) ConfigureProvider(n string, cfg *ResourceConfig) error | |||
134 | return c.ConfigureProviderError | 132 | return c.ConfigureProviderError |
135 | } | 133 | } |
136 | 134 | ||
137 | func (c *MockEvalContext) SetProviderConfig( | ||
138 | n string, cfg *ResourceConfig) error { | ||
139 | c.SetProviderConfigCalled = true | ||
140 | c.SetProviderConfigName = n | ||
141 | c.SetProviderConfigConfig = cfg | ||
142 | return nil | ||
143 | } | ||
144 | |||
145 | func (c *MockEvalContext) ParentProviderConfig(n string) *ResourceConfig { | ||
146 | c.ParentProviderConfigCalled = true | ||
147 | c.ParentProviderConfigName = n | ||
148 | return c.ParentProviderConfigConfig | ||
149 | } | ||
150 | |||
151 | func (c *MockEvalContext) ProviderInput(n string) map[string]interface{} { | 135 | func (c *MockEvalContext) ProviderInput(n string) map[string]interface{} { |
152 | c.ProviderInputCalled = true | 136 | c.ProviderInputCalled = true |
153 | c.ProviderInputName = n | 137 | c.ProviderInputName = n |
@@ -186,6 +170,14 @@ func (c *MockEvalContext) Interpolate( | |||
186 | return c.InterpolateConfigResult, c.InterpolateError | 170 | return c.InterpolateConfigResult, c.InterpolateError |
187 | } | 171 | } |
188 | 172 | ||
173 | func (c *MockEvalContext) InterpolateProvider( | ||
174 | config *config.ProviderConfig, resource *Resource) (*ResourceConfig, error) { | ||
175 | c.InterpolateProviderCalled = true | ||
176 | c.InterpolateProviderConfig = config | ||
177 | c.InterpolateProviderResource = resource | ||
178 | return c.InterpolateProviderConfigResult, c.InterpolateError | ||
179 | } | ||
180 | |||
189 | func (c *MockEvalContext) Path() []string { | 181 | func (c *MockEvalContext) Path() []string { |
190 | c.PathCalled = true | 182 | c.PathCalled = true |
191 | return c.PathPath | 183 | return c.PathPath |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go b/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go index c35f908..26205ce 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_diff.go | |||
@@ -6,6 +6,7 @@ import ( | |||
6 | "strings" | 6 | "strings" |
7 | 7 | ||
8 | "github.com/hashicorp/terraform/config" | 8 | "github.com/hashicorp/terraform/config" |
9 | "github.com/hashicorp/terraform/version" | ||
9 | ) | 10 | ) |
10 | 11 | ||
11 | // EvalCompareDiff is an EvalNode implementation that compares two diffs | 12 | // EvalCompareDiff is an EvalNode implementation that compares two diffs |
@@ -60,7 +61,7 @@ func (n *EvalCompareDiff) Eval(ctx EvalContext) (interface{}, error) { | |||
60 | "\n"+ | 61 | "\n"+ |
61 | "Also include as much context as you can about your config, state, "+ | 62 | "Also include as much context as you can about your config, state, "+ |
62 | "and the steps you performed to trigger this error.\n", | 63 | "and the steps you performed to trigger this error.\n", |
63 | n.Info.Id, Version, n.Info.Id, reason, one, two) | 64 | n.Info.Id, version.Version, n.Info.Id, reason, one, two) |
64 | } | 65 | } |
65 | 66 | ||
66 | return nil, nil | 67 | return nil, nil |
@@ -255,11 +256,15 @@ func (n *EvalDiff) processIgnoreChanges(diff *InstanceDiff) error { | |||
255 | containers := groupContainers(diff) | 256 | containers := groupContainers(diff) |
256 | keep := map[string]bool{} | 257 | keep := map[string]bool{} |
257 | for _, v := range containers { | 258 | for _, v := range containers { |
258 | if v.keepDiff() { | 259 | if v.keepDiff(ignorableAttrKeys) { |
259 | // At least one key has changes, so list all the sibling keys | 260 | // At least one key has changes, so list all the sibling keys |
260 | // to keep in the diff. | 261 | // to keep in the diff |
261 | for k := range v { | 262 | for k := range v { |
262 | keep[k] = true | 263 | keep[k] = true |
264 | // this key may have been added by the user to ignore, but | ||
265 | // if it's a subkey in a container, we need to un-ignore it | ||
266 | // to keep the complete containter. | ||
267 | delete(ignorableAttrKeys, k) | ||
263 | } | 268 | } |
264 | } | 269 | } |
265 | } | 270 | } |
@@ -291,10 +296,17 @@ func (n *EvalDiff) processIgnoreChanges(diff *InstanceDiff) error { | |||
291 | // a group of key-*ResourceAttrDiff pairs from the same flatmapped container | 296 | // a group of key-*ResourceAttrDiff pairs from the same flatmapped container |
292 | type flatAttrDiff map[string]*ResourceAttrDiff | 297 | type flatAttrDiff map[string]*ResourceAttrDiff |
293 | 298 | ||
294 | // we need to keep all keys if any of them have a diff | 299 | // we need to keep all keys if any of them have a diff that's not ignored |
295 | func (f flatAttrDiff) keepDiff() bool { | 300 | func (f flatAttrDiff) keepDiff(ignoreChanges map[string]bool) bool { |
296 | for _, v := range f { | 301 | for k, v := range f { |
297 | if !v.Empty() && !v.NewComputed { | 302 | ignore := false |
303 | for attr := range ignoreChanges { | ||
304 | if strings.HasPrefix(k, attr) { | ||
305 | ignore = true | ||
306 | } | ||
307 | } | ||
308 | |||
309 | if !v.Empty() && !v.NewComputed && !ignore { | ||
298 | return true | 310 | return true |
299 | } | 311 | } |
300 | } | 312 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_interpolate.go b/vendor/github.com/hashicorp/terraform/terraform/eval_interpolate.go index 6825ff5..6a78a6b 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_interpolate.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_interpolate.go | |||
@@ -1,18 +1,50 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import "github.com/hashicorp/terraform/config" | 3 | import ( |
4 | "log" | ||
5 | |||
6 | "github.com/hashicorp/terraform/config" | ||
7 | ) | ||
4 | 8 | ||
5 | // EvalInterpolate is an EvalNode implementation that takes a raw | 9 | // EvalInterpolate is an EvalNode implementation that takes a raw |
6 | // configuration and interpolates it. | 10 | // configuration and interpolates it. |
7 | type EvalInterpolate struct { | 11 | type EvalInterpolate struct { |
8 | Config *config.RawConfig | 12 | Config *config.RawConfig |
9 | Resource *Resource | 13 | Resource *Resource |
10 | Output **ResourceConfig | 14 | Output **ResourceConfig |
15 | ContinueOnErr bool | ||
11 | } | 16 | } |
12 | 17 | ||
13 | func (n *EvalInterpolate) Eval(ctx EvalContext) (interface{}, error) { | 18 | func (n *EvalInterpolate) Eval(ctx EvalContext) (interface{}, error) { |
14 | rc, err := ctx.Interpolate(n.Config, n.Resource) | 19 | rc, err := ctx.Interpolate(n.Config, n.Resource) |
15 | if err != nil { | 20 | if err != nil { |
21 | if n.ContinueOnErr { | ||
22 | log.Printf("[WARN] Interpolation %q failed: %s", n.Config.Key, err) | ||
23 | return nil, EvalEarlyExitError{} | ||
24 | } | ||
25 | return nil, err | ||
26 | } | ||
27 | |||
28 | if n.Output != nil { | ||
29 | *n.Output = rc | ||
30 | } | ||
31 | |||
32 | return nil, nil | ||
33 | } | ||
34 | |||
35 | // EvalInterpolateProvider is an EvalNode implementation that takes a | ||
36 | // ProviderConfig and interpolates it. Provider configurations are the only | ||
37 | // "inherited" type of configuration we have, and the original raw config may | ||
38 | // have a different interpolation scope. | ||
39 | type EvalInterpolateProvider struct { | ||
40 | Config *config.ProviderConfig | ||
41 | Resource *Resource | ||
42 | Output **ResourceConfig | ||
43 | } | ||
44 | |||
45 | func (n *EvalInterpolateProvider) Eval(ctx EvalContext) (interface{}, error) { | ||
46 | rc, err := ctx.InterpolateProvider(n.Config, n.Resource) | ||
47 | if err != nil { | ||
16 | return nil, err | 48 | return nil, err |
17 | } | 49 | } |
18 | 50 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_local.go b/vendor/github.com/hashicorp/terraform/terraform/eval_local.go new file mode 100644 index 0000000..a4b2a50 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_local.go | |||
@@ -0,0 +1,86 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/terraform/config" | ||
7 | ) | ||
8 | |||
9 | // EvalLocal is an EvalNode implementation that evaluates the | ||
10 | // expression for a local value and writes it into a transient part of | ||
11 | // the state. | ||
12 | type EvalLocal struct { | ||
13 | Name string | ||
14 | Value *config.RawConfig | ||
15 | } | ||
16 | |||
17 | func (n *EvalLocal) Eval(ctx EvalContext) (interface{}, error) { | ||
18 | cfg, err := ctx.Interpolate(n.Value, nil) | ||
19 | if err != nil { | ||
20 | return nil, fmt.Errorf("local.%s: %s", n.Name, err) | ||
21 | } | ||
22 | |||
23 | state, lock := ctx.State() | ||
24 | if state == nil { | ||
25 | return nil, fmt.Errorf("cannot write local value to nil state") | ||
26 | } | ||
27 | |||
28 | // Get a write lock so we can access the state | ||
29 | lock.Lock() | ||
30 | defer lock.Unlock() | ||
31 | |||
32 | // Look for the module state. If we don't have one, create it. | ||
33 | mod := state.ModuleByPath(ctx.Path()) | ||
34 | if mod == nil { | ||
35 | mod = state.AddModule(ctx.Path()) | ||
36 | } | ||
37 | |||
38 | // Get the value from the config | ||
39 | var valueRaw interface{} = config.UnknownVariableValue | ||
40 | if cfg != nil { | ||
41 | var ok bool | ||
42 | valueRaw, ok = cfg.Get("value") | ||
43 | if !ok { | ||
44 | valueRaw = "" | ||
45 | } | ||
46 | if cfg.IsComputed("value") { | ||
47 | valueRaw = config.UnknownVariableValue | ||
48 | } | ||
49 | } | ||
50 | |||
51 | if mod.Locals == nil { | ||
52 | // initialize | ||
53 | mod.Locals = map[string]interface{}{} | ||
54 | } | ||
55 | mod.Locals[n.Name] = valueRaw | ||
56 | |||
57 | return nil, nil | ||
58 | } | ||
59 | |||
60 | // EvalDeleteLocal is an EvalNode implementation that deletes a Local value | ||
61 | // from the state. Locals aren't persisted, but we don't need to evaluate them | ||
62 | // during destroy. | ||
63 | type EvalDeleteLocal struct { | ||
64 | Name string | ||
65 | } | ||
66 | |||
67 | func (n *EvalDeleteLocal) Eval(ctx EvalContext) (interface{}, error) { | ||
68 | state, lock := ctx.State() | ||
69 | if state == nil { | ||
70 | return nil, nil | ||
71 | } | ||
72 | |||
73 | // Get a write lock so we can access this instance | ||
74 | lock.Lock() | ||
75 | defer lock.Unlock() | ||
76 | |||
77 | // Look for the module state. If we don't have one, create it. | ||
78 | mod := state.ModuleByPath(ctx.Path()) | ||
79 | if mod == nil { | ||
80 | return nil, nil | ||
81 | } | ||
82 | |||
83 | delete(mod.Locals, n.Name) | ||
84 | |||
85 | return nil, nil | ||
86 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_output.go b/vendor/github.com/hashicorp/terraform/terraform/eval_output.go index cf61781..a834627 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_output.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_output.go | |||
@@ -41,15 +41,16 @@ type EvalWriteOutput struct { | |||
41 | Name string | 41 | Name string |
42 | Sensitive bool | 42 | Sensitive bool |
43 | Value *config.RawConfig | 43 | Value *config.RawConfig |
44 | // ContinueOnErr allows interpolation to fail during Input | ||
45 | ContinueOnErr bool | ||
44 | } | 46 | } |
45 | 47 | ||
46 | // TODO: test | 48 | // TODO: test |
47 | func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) { | 49 | func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) { |
50 | // This has to run before we have a state lock, since interpolation also | ||
51 | // reads the state | ||
48 | cfg, err := ctx.Interpolate(n.Value, nil) | 52 | cfg, err := ctx.Interpolate(n.Value, nil) |
49 | if err != nil { | 53 | // handle the error after we have the module from the state |
50 | // Log error but continue anyway | ||
51 | log.Printf("[WARN] Output interpolation %q failed: %s", n.Name, err) | ||
52 | } | ||
53 | 54 | ||
54 | state, lock := ctx.State() | 55 | state, lock := ctx.State() |
55 | if state == nil { | 56 | if state == nil { |
@@ -59,13 +60,27 @@ func (n *EvalWriteOutput) Eval(ctx EvalContext) (interface{}, error) { | |||
59 | // Get a write lock so we can access this instance | 60 | // Get a write lock so we can access this instance |
60 | lock.Lock() | 61 | lock.Lock() |
61 | defer lock.Unlock() | 62 | defer lock.Unlock() |
62 | |||
63 | // Look for the module state. If we don't have one, create it. | 63 | // Look for the module state. If we don't have one, create it. |
64 | mod := state.ModuleByPath(ctx.Path()) | 64 | mod := state.ModuleByPath(ctx.Path()) |
65 | if mod == nil { | 65 | if mod == nil { |
66 | mod = state.AddModule(ctx.Path()) | 66 | mod = state.AddModule(ctx.Path()) |
67 | } | 67 | } |
68 | 68 | ||
69 | // handling the interpolation error | ||
70 | if err != nil { | ||
71 | if n.ContinueOnErr || flagWarnOutputErrors { | ||
72 | log.Printf("[ERROR] Output interpolation %q failed: %s", n.Name, err) | ||
73 | // if we're continuing, make sure the output is included, and | ||
74 | // marked as unknown | ||
75 | mod.Outputs[n.Name] = &OutputState{ | ||
76 | Type: "string", | ||
77 | Value: config.UnknownVariableValue, | ||
78 | } | ||
79 | return nil, EvalEarlyExitError{} | ||
80 | } | ||
81 | return nil, err | ||
82 | } | ||
83 | |||
69 | // Get the value from the config | 84 | // Get the value from the config |
70 | var valueRaw interface{} = config.UnknownVariableValue | 85 | var valueRaw interface{} = config.UnknownVariableValue |
71 | if cfg != nil { | 86 | if cfg != nil { |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go b/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go index 092fd18..61f6ff9 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_provider.go | |||
@@ -6,17 +6,6 @@ import ( | |||
6 | "github.com/hashicorp/terraform/config" | 6 | "github.com/hashicorp/terraform/config" |
7 | ) | 7 | ) |
8 | 8 | ||
9 | // EvalSetProviderConfig sets the parent configuration for a provider | ||
10 | // without configuring that provider, validating it, etc. | ||
11 | type EvalSetProviderConfig struct { | ||
12 | Provider string | ||
13 | Config **ResourceConfig | ||
14 | } | ||
15 | |||
16 | func (n *EvalSetProviderConfig) Eval(ctx EvalContext) (interface{}, error) { | ||
17 | return nil, ctx.SetProviderConfig(n.Provider, *n.Config) | ||
18 | } | ||
19 | |||
20 | // EvalBuildProviderConfig outputs a *ResourceConfig that is properly | 9 | // EvalBuildProviderConfig outputs a *ResourceConfig that is properly |
21 | // merged with parents and inputs on top of what is configured in the file. | 10 | // merged with parents and inputs on top of what is configured in the file. |
22 | type EvalBuildProviderConfig struct { | 11 | type EvalBuildProviderConfig struct { |
@@ -28,7 +17,7 @@ type EvalBuildProviderConfig struct { | |||
28 | func (n *EvalBuildProviderConfig) Eval(ctx EvalContext) (interface{}, error) { | 17 | func (n *EvalBuildProviderConfig) Eval(ctx EvalContext) (interface{}, error) { |
29 | cfg := *n.Config | 18 | cfg := *n.Config |
30 | 19 | ||
31 | // If we have a configuration set, then merge that in | 20 | // If we have an Input configuration set, then merge that in |
32 | if input := ctx.ProviderInput(n.Provider); input != nil { | 21 | if input := ctx.ProviderInput(n.Provider); input != nil { |
33 | // "input" is a map of the subset of config values that were known | 22 | // "input" is a map of the subset of config values that were known |
34 | // during the input walk, set by EvalInputProvider. Note that | 23 | // during the input walk, set by EvalInputProvider. Note that |
@@ -40,13 +29,7 @@ func (n *EvalBuildProviderConfig) Eval(ctx EvalContext) (interface{}, error) { | |||
40 | return nil, err | 29 | return nil, err |
41 | } | 30 | } |
42 | 31 | ||
43 | merged := cfg.raw.Merge(rc) | 32 | merged := rc.Merge(cfg.raw) |
44 | cfg = NewResourceConfig(merged) | ||
45 | } | ||
46 | |||
47 | // Get the parent configuration if there is one | ||
48 | if parent := ctx.ParentProviderConfig(n.Provider); parent != nil { | ||
49 | merged := cfg.raw.Merge(parent.raw) | ||
50 | cfg = NewResourceConfig(merged) | 33 | cfg = NewResourceConfig(merged) |
51 | } | 34 | } |
52 | 35 | ||
@@ -69,11 +52,12 @@ func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) { | |||
69 | // and returns nothing. The provider can be retrieved again with the | 52 | // and returns nothing. The provider can be retrieved again with the |
70 | // EvalGetProvider node. | 53 | // EvalGetProvider node. |
71 | type EvalInitProvider struct { | 54 | type EvalInitProvider struct { |
72 | Name string | 55 | TypeName string |
56 | Name string | ||
73 | } | 57 | } |
74 | 58 | ||
75 | func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) { | 59 | func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) { |
76 | return ctx.InitProvider(n.Name) | 60 | return ctx.InitProvider(n.TypeName, n.Name) |
77 | } | 61 | } |
78 | 62 | ||
79 | // EvalCloseProvider is an EvalNode implementation that closes provider | 63 | // EvalCloseProvider is an EvalNode implementation that closes provider |
@@ -116,12 +100,8 @@ type EvalInputProvider struct { | |||
116 | } | 100 | } |
117 | 101 | ||
118 | func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) { | 102 | func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) { |
119 | // If we already configured this provider, then don't do this again | ||
120 | if v := ctx.ProviderInput(n.Name); v != nil { | ||
121 | return nil, nil | ||
122 | } | ||
123 | |||
124 | rc := *n.Config | 103 | rc := *n.Config |
104 | orig := rc.DeepCopy() | ||
125 | 105 | ||
126 | // Wrap the input into a namespace | 106 | // Wrap the input into a namespace |
127 | input := &PrefixUIInput{ | 107 | input := &PrefixUIInput{ |
@@ -138,27 +118,20 @@ func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) { | |||
138 | "Error configuring %s: %s", n.Name, err) | 118 | "Error configuring %s: %s", n.Name, err) |
139 | } | 119 | } |
140 | 120 | ||
141 | // Set the input that we received so that child modules don't attempt | 121 | // We only store values that have changed through Input. |
142 | // to ask for input again. | 122 | // The goal is to cache cache input responses, not to provide a complete |
123 | // config for other providers. | ||
124 | confMap := make(map[string]interface{}) | ||
143 | if config != nil && len(config.Config) > 0 { | 125 | if config != nil && len(config.Config) > 0 { |
144 | // This repository of provider input results on the context doesn't | 126 | // any values that weren't in the original ResourcConfig will be cached |
145 | // retain config.ComputedKeys, so we need to filter those out here | 127 | for k, v := range config.Config { |
146 | // in order that later users of this data won't try to use the unknown | 128 | if _, ok := orig.Config[k]; !ok { |
147 | // value placeholder as if it were a literal value. This map is just | 129 | confMap[k] = v |
148 | // of known values we've been able to complete so far; dynamic stuff | ||
149 | // will be merged in by EvalBuildProviderConfig on subsequent | ||
150 | // (post-input) walks. | ||
151 | confMap := config.Config | ||
152 | if config.ComputedKeys != nil { | ||
153 | for _, key := range config.ComputedKeys { | ||
154 | delete(confMap, key) | ||
155 | } | 130 | } |
156 | } | 131 | } |
157 | |||
158 | ctx.SetProviderInput(n.Name, confMap) | ||
159 | } else { | ||
160 | ctx.SetProviderInput(n.Name, map[string]interface{}{}) | ||
161 | } | 132 | } |
162 | 133 | ||
134 | ctx.SetProviderInput(n.Name, confMap) | ||
135 | |||
163 | return nil, nil | 136 | return nil, nil |
164 | } | 137 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_state.go b/vendor/github.com/hashicorp/terraform/terraform/eval_state.go index 126a0e6..1182690 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_state.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_state.go | |||
@@ -1,6 +1,8 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import "fmt" | 3 | import ( |
4 | "fmt" | ||
5 | ) | ||
4 | 6 | ||
5 | // EvalReadState is an EvalNode implementation that reads the | 7 | // EvalReadState is an EvalNode implementation that reads the |
6 | // primary InstanceState for a specific resource out of the state. | 8 | // primary InstanceState for a specific resource out of the state. |
@@ -212,37 +214,6 @@ func writeInstanceToState( | |||
212 | return nil, nil | 214 | return nil, nil |
213 | } | 215 | } |
214 | 216 | ||
215 | // EvalClearPrimaryState is an EvalNode implementation that clears the primary | ||
216 | // instance from a resource state. | ||
217 | type EvalClearPrimaryState struct { | ||
218 | Name string | ||
219 | } | ||
220 | |||
221 | func (n *EvalClearPrimaryState) Eval(ctx EvalContext) (interface{}, error) { | ||
222 | state, lock := ctx.State() | ||
223 | |||
224 | // Get a read lock so we can access this instance | ||
225 | lock.RLock() | ||
226 | defer lock.RUnlock() | ||
227 | |||
228 | // Look for the module state. If we don't have one, then it doesn't matter. | ||
229 | mod := state.ModuleByPath(ctx.Path()) | ||
230 | if mod == nil { | ||
231 | return nil, nil | ||
232 | } | ||
233 | |||
234 | // Look for the resource state. If we don't have one, then it is okay. | ||
235 | rs := mod.Resources[n.Name] | ||
236 | if rs == nil { | ||
237 | return nil, nil | ||
238 | } | ||
239 | |||
240 | // Clear primary from the resource state | ||
241 | rs.Primary = nil | ||
242 | |||
243 | return nil, nil | ||
244 | } | ||
245 | |||
246 | // EvalDeposeState is an EvalNode implementation that takes the primary | 217 | // EvalDeposeState is an EvalNode implementation that takes the primary |
247 | // out of a state and makes it Deposed. This is done at the beginning of | 218 | // out of a state and makes it Deposed. This is done at the beginning of |
248 | // create-before-destroy calls so that the create can create while preserving | 219 | // create-before-destroy calls so that the create can create while preserving |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go b/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go index 478aa64..3e5a84c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go +++ b/vendor/github.com/hashicorp/terraform/terraform/eval_validate.go | |||
@@ -144,16 +144,20 @@ func (n *EvalValidateProvisioner) validateConnConfig(connConfig *ResourceConfig) | |||
144 | 144 | ||
145 | // For type=ssh only (enforced in ssh communicator) | 145 | // For type=ssh only (enforced in ssh communicator) |
146 | PrivateKey interface{} `mapstructure:"private_key"` | 146 | PrivateKey interface{} `mapstructure:"private_key"` |
147 | HostKey interface{} `mapstructure:"host_key"` | ||
147 | Agent interface{} `mapstructure:"agent"` | 148 | Agent interface{} `mapstructure:"agent"` |
148 | BastionHost interface{} `mapstructure:"bastion_host"` | 149 | BastionHost interface{} `mapstructure:"bastion_host"` |
150 | BastionHostKey interface{} `mapstructure:"bastion_host_key"` | ||
149 | BastionPort interface{} `mapstructure:"bastion_port"` | 151 | BastionPort interface{} `mapstructure:"bastion_port"` |
150 | BastionUser interface{} `mapstructure:"bastion_user"` | 152 | BastionUser interface{} `mapstructure:"bastion_user"` |
151 | BastionPassword interface{} `mapstructure:"bastion_password"` | 153 | BastionPassword interface{} `mapstructure:"bastion_password"` |
152 | BastionPrivateKey interface{} `mapstructure:"bastion_private_key"` | 154 | BastionPrivateKey interface{} `mapstructure:"bastion_private_key"` |
155 | AgentIdentity interface{} `mapstructure:"agent_identity"` | ||
153 | 156 | ||
154 | // For type=winrm only (enforced in winrm communicator) | 157 | // For type=winrm only (enforced in winrm communicator) |
155 | HTTPS interface{} `mapstructure:"https"` | 158 | HTTPS interface{} `mapstructure:"https"` |
156 | Insecure interface{} `mapstructure:"insecure"` | 159 | Insecure interface{} `mapstructure:"insecure"` |
160 | NTLM interface{} `mapstructure:"use_ntlm"` | ||
157 | CACert interface{} `mapstructure:"cacert"` | 161 | CACert interface{} `mapstructure:"cacert"` |
158 | } | 162 | } |
159 | 163 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/evaltree_provider.go b/vendor/github.com/hashicorp/terraform/terraform/evaltree_provider.go index 00392ef..0c3da48 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/evaltree_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/evaltree_provider.go | |||
@@ -1,17 +1,24 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "strings" | ||
5 | |||
4 | "github.com/hashicorp/terraform/config" | 6 | "github.com/hashicorp/terraform/config" |
5 | ) | 7 | ) |
6 | 8 | ||
7 | // ProviderEvalTree returns the evaluation tree for initializing and | 9 | // ProviderEvalTree returns the evaluation tree for initializing and |
8 | // configuring providers. | 10 | // configuring providers. |
9 | func ProviderEvalTree(n string, config *config.RawConfig) EvalNode { | 11 | func ProviderEvalTree(n *NodeApplyableProvider, config *config.ProviderConfig) EvalNode { |
10 | var provider ResourceProvider | 12 | var provider ResourceProvider |
11 | var resourceConfig *ResourceConfig | 13 | var resourceConfig *ResourceConfig |
12 | 14 | ||
15 | typeName := strings.SplitN(n.NameValue, ".", 2)[0] | ||
16 | |||
13 | seq := make([]EvalNode, 0, 5) | 17 | seq := make([]EvalNode, 0, 5) |
14 | seq = append(seq, &EvalInitProvider{Name: n}) | 18 | seq = append(seq, &EvalInitProvider{ |
19 | TypeName: typeName, | ||
20 | Name: n.Name(), | ||
21 | }) | ||
15 | 22 | ||
16 | // Input stuff | 23 | // Input stuff |
17 | seq = append(seq, &EvalOpFilter{ | 24 | seq = append(seq, &EvalOpFilter{ |
@@ -19,20 +26,20 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode { | |||
19 | Node: &EvalSequence{ | 26 | Node: &EvalSequence{ |
20 | Nodes: []EvalNode{ | 27 | Nodes: []EvalNode{ |
21 | &EvalGetProvider{ | 28 | &EvalGetProvider{ |
22 | Name: n, | 29 | Name: n.Name(), |
23 | Output: &provider, | 30 | Output: &provider, |
24 | }, | 31 | }, |
25 | &EvalInterpolate{ | 32 | &EvalInterpolateProvider{ |
26 | Config: config, | 33 | Config: config, |
27 | Output: &resourceConfig, | 34 | Output: &resourceConfig, |
28 | }, | 35 | }, |
29 | &EvalBuildProviderConfig{ | 36 | &EvalBuildProviderConfig{ |
30 | Provider: n, | 37 | Provider: n.NameValue, |
31 | Config: &resourceConfig, | 38 | Config: &resourceConfig, |
32 | Output: &resourceConfig, | 39 | Output: &resourceConfig, |
33 | }, | 40 | }, |
34 | &EvalInputProvider{ | 41 | &EvalInputProvider{ |
35 | Name: n, | 42 | Name: n.NameValue, |
36 | Provider: &provider, | 43 | Provider: &provider, |
37 | Config: &resourceConfig, | 44 | Config: &resourceConfig, |
38 | }, | 45 | }, |
@@ -45,15 +52,15 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode { | |||
45 | Node: &EvalSequence{ | 52 | Node: &EvalSequence{ |
46 | Nodes: []EvalNode{ | 53 | Nodes: []EvalNode{ |
47 | &EvalGetProvider{ | 54 | &EvalGetProvider{ |
48 | Name: n, | 55 | Name: n.Name(), |
49 | Output: &provider, | 56 | Output: &provider, |
50 | }, | 57 | }, |
51 | &EvalInterpolate{ | 58 | &EvalInterpolateProvider{ |
52 | Config: config, | 59 | Config: config, |
53 | Output: &resourceConfig, | 60 | Output: &resourceConfig, |
54 | }, | 61 | }, |
55 | &EvalBuildProviderConfig{ | 62 | &EvalBuildProviderConfig{ |
56 | Provider: n, | 63 | Provider: n.NameValue, |
57 | Config: &resourceConfig, | 64 | Config: &resourceConfig, |
58 | Output: &resourceConfig, | 65 | Output: &resourceConfig, |
59 | }, | 66 | }, |
@@ -61,10 +68,6 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode { | |||
61 | Provider: &provider, | 68 | Provider: &provider, |
62 | Config: &resourceConfig, | 69 | Config: &resourceConfig, |
63 | }, | 70 | }, |
64 | &EvalSetProviderConfig{ | ||
65 | Provider: n, | ||
66 | Config: &resourceConfig, | ||
67 | }, | ||
68 | }, | 71 | }, |
69 | }, | 72 | }, |
70 | }) | 73 | }) |
@@ -75,22 +78,18 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode { | |||
75 | Node: &EvalSequence{ | 78 | Node: &EvalSequence{ |
76 | Nodes: []EvalNode{ | 79 | Nodes: []EvalNode{ |
77 | &EvalGetProvider{ | 80 | &EvalGetProvider{ |
78 | Name: n, | 81 | Name: n.Name(), |
79 | Output: &provider, | 82 | Output: &provider, |
80 | }, | 83 | }, |
81 | &EvalInterpolate{ | 84 | &EvalInterpolateProvider{ |
82 | Config: config, | 85 | Config: config, |
83 | Output: &resourceConfig, | 86 | Output: &resourceConfig, |
84 | }, | 87 | }, |
85 | &EvalBuildProviderConfig{ | 88 | &EvalBuildProviderConfig{ |
86 | Provider: n, | 89 | Provider: n.NameValue, |
87 | Config: &resourceConfig, | 90 | Config: &resourceConfig, |
88 | Output: &resourceConfig, | 91 | Output: &resourceConfig, |
89 | }, | 92 | }, |
90 | &EvalSetProviderConfig{ | ||
91 | Provider: n, | ||
92 | Config: &resourceConfig, | ||
93 | }, | ||
94 | }, | 93 | }, |
95 | }, | 94 | }, |
96 | }) | 95 | }) |
@@ -102,7 +101,7 @@ func ProviderEvalTree(n string, config *config.RawConfig) EvalNode { | |||
102 | Node: &EvalSequence{ | 101 | Node: &EvalSequence{ |
103 | Nodes: []EvalNode{ | 102 | Nodes: []EvalNode{ |
104 | &EvalConfigProvider{ | 103 | &EvalConfigProvider{ |
105 | Provider: n, | 104 | Provider: n.Name(), |
106 | Config: &resourceConfig, | 105 | Config: &resourceConfig, |
107 | }, | 106 | }, |
108 | }, | 107 | }, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/features.go b/vendor/github.com/hashicorp/terraform/terraform/features.go new file mode 100644 index 0000000..97c77bd --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/features.go | |||
@@ -0,0 +1,7 @@ | |||
1 | package terraform | ||
2 | |||
3 | import "os" | ||
4 | |||
5 | // This file holds feature flags for the next release | ||
6 | |||
7 | var flagWarnOutputErrors = os.Getenv("TF_WARN_OUTPUT_ERRORS") != "" | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph.go b/vendor/github.com/hashicorp/terraform/terraform/graph.go index 48ce6a3..735ec4e 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph.go | |||
@@ -70,7 +70,7 @@ func (g *Graph) walk(walker GraphWalker) error { | |||
70 | // Walk the graph. | 70 | // Walk the graph. |
71 | var walkFn dag.WalkFunc | 71 | var walkFn dag.WalkFunc |
72 | walkFn = func(v dag.Vertex) (rerr error) { | 72 | walkFn = func(v dag.Vertex) (rerr error) { |
73 | log.Printf("[DEBUG] vertex '%s.%s': walking", path, dag.VertexName(v)) | 73 | log.Printf("[TRACE] vertex '%s.%s': walking", path, dag.VertexName(v)) |
74 | g.DebugVisitInfo(v, g.debugName) | 74 | g.DebugVisitInfo(v, g.debugName) |
75 | 75 | ||
76 | // If we have a panic wrap GraphWalker and a panic occurs, recover | 76 | // If we have a panic wrap GraphWalker and a panic occurs, recover |
@@ -118,7 +118,7 @@ func (g *Graph) walk(walker GraphWalker) error { | |||
118 | 118 | ||
119 | // Allow the walker to change our tree if needed. Eval, | 119 | // Allow the walker to change our tree if needed. Eval, |
120 | // then callback with the output. | 120 | // then callback with the output. |
121 | log.Printf("[DEBUG] vertex '%s.%s': evaluating", path, dag.VertexName(v)) | 121 | log.Printf("[TRACE] vertex '%s.%s': evaluating", path, dag.VertexName(v)) |
122 | 122 | ||
123 | g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path)) | 123 | g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path)) |
124 | 124 | ||
@@ -132,7 +132,7 @@ func (g *Graph) walk(walker GraphWalker) error { | |||
132 | // If the node is dynamically expanded, then expand it | 132 | // If the node is dynamically expanded, then expand it |
133 | if ev, ok := v.(GraphNodeDynamicExpandable); ok { | 133 | if ev, ok := v.(GraphNodeDynamicExpandable); ok { |
134 | log.Printf( | 134 | log.Printf( |
135 | "[DEBUG] vertex '%s.%s': expanding/walking dynamic subgraph", | 135 | "[TRACE] vertex '%s.%s': expanding/walking dynamic subgraph", |
136 | path, | 136 | path, |
137 | dag.VertexName(v)) | 137 | dag.VertexName(v)) |
138 | 138 | ||
@@ -154,7 +154,7 @@ func (g *Graph) walk(walker GraphWalker) error { | |||
154 | // If the node has a subgraph, then walk the subgraph | 154 | // If the node has a subgraph, then walk the subgraph |
155 | if sn, ok := v.(GraphNodeSubgraph); ok { | 155 | if sn, ok := v.(GraphNodeSubgraph); ok { |
156 | log.Printf( | 156 | log.Printf( |
157 | "[DEBUG] vertex '%s.%s': walking subgraph", | 157 | "[TRACE] vertex '%s.%s': walking subgraph", |
158 | path, | 158 | path, |
159 | dag.VertexName(v)) | 159 | dag.VertexName(v)) |
160 | 160 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go index 38a90f2..0c2b233 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_apply.go | |||
@@ -87,12 +87,8 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { | |||
87 | // Attach the state | 87 | // Attach the state |
88 | &AttachStateTransformer{State: b.State}, | 88 | &AttachStateTransformer{State: b.State}, |
89 | 89 | ||
90 | // Create all the providers | 90 | // add providers |
91 | &MissingProviderTransformer{Providers: b.Providers, Concrete: concreteProvider}, | 91 | TransformProviders(b.Providers, concreteProvider, b.Module), |
92 | &ProviderTransformer{}, | ||
93 | &DisableProviderTransformer{}, | ||
94 | &ParentProviderTransformer{}, | ||
95 | &AttachProviderConfigTransformer{Module: b.Module}, | ||
96 | 92 | ||
97 | // Destruction ordering | 93 | // Destruction ordering |
98 | &DestroyEdgeTransformer{Module: b.Module, State: b.State}, | 94 | &DestroyEdgeTransformer{Module: b.Module, State: b.State}, |
@@ -108,15 +104,36 @@ func (b *ApplyGraphBuilder) Steps() []GraphTransformer { | |||
108 | // Add root variables | 104 | // Add root variables |
109 | &RootVariableTransformer{Module: b.Module}, | 105 | &RootVariableTransformer{Module: b.Module}, |
110 | 106 | ||
107 | // Add the local values | ||
108 | &LocalTransformer{Module: b.Module}, | ||
109 | |||
111 | // Add the outputs | 110 | // Add the outputs |
112 | &OutputTransformer{Module: b.Module}, | 111 | &OutputTransformer{Module: b.Module}, |
113 | 112 | ||
114 | // Add module variables | 113 | // Add module variables |
115 | &ModuleVariableTransformer{Module: b.Module}, | 114 | &ModuleVariableTransformer{Module: b.Module}, |
116 | 115 | ||
116 | // Remove modules no longer present in the config | ||
117 | &RemovedModuleTransformer{Module: b.Module, State: b.State}, | ||
118 | |||
117 | // Connect references so ordering is correct | 119 | // Connect references so ordering is correct |
118 | &ReferenceTransformer{}, | 120 | &ReferenceTransformer{}, |
119 | 121 | ||
122 | // Handle destroy time transformations for output and local values. | ||
123 | // Reverse the edges from outputs and locals, so that | ||
124 | // interpolations don't fail during destroy. | ||
125 | // Create a destroy node for outputs to remove them from the state. | ||
126 | // Prune unreferenced values, which may have interpolations that can't | ||
127 | // be resolved. | ||
128 | GraphTransformIf( | ||
129 | func() bool { return b.Destroy }, | ||
130 | GraphTransformMulti( | ||
131 | &DestroyValueReferenceTransformer{}, | ||
132 | &DestroyOutputTransformer{}, | ||
133 | &PruneUnusedValuesTransformer{}, | ||
134 | ), | ||
135 | ), | ||
136 | |||
120 | // Add the node to fix the state count boundaries | 137 | // Add the node to fix the state count boundaries |
121 | &CountBoundaryTransformer{}, | 138 | &CountBoundaryTransformer{}, |
122 | 139 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go index 7070c59..07a1eaf 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_import.go | |||
@@ -52,12 +52,7 @@ func (b *ImportGraphBuilder) Steps() []GraphTransformer { | |||
52 | // Add the import steps | 52 | // Add the import steps |
53 | &ImportStateTransformer{Targets: b.ImportTargets}, | 53 | &ImportStateTransformer{Targets: b.ImportTargets}, |
54 | 54 | ||
55 | // Provider-related transformations | 55 | TransformProviders(b.Providers, concreteProvider, mod), |
56 | &MissingProviderTransformer{Providers: b.Providers, Concrete: concreteProvider}, | ||
57 | &ProviderTransformer{}, | ||
58 | &DisableProviderTransformer{}, | ||
59 | &ParentProviderTransformer{}, | ||
60 | &AttachProviderConfigTransformer{Module: mod}, | ||
61 | 56 | ||
62 | // This validates that the providers only depend on variables | 57 | // This validates that the providers only depend on variables |
63 | &ImportProviderValidateTransformer{}, | 58 | &ImportProviderValidateTransformer{}, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go index 4b29bbb..f8dd0fc 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_plan.go | |||
@@ -71,6 +71,9 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { | |||
71 | Module: b.Module, | 71 | Module: b.Module, |
72 | }, | 72 | }, |
73 | 73 | ||
74 | // Add the local values | ||
75 | &LocalTransformer{Module: b.Module}, | ||
76 | |||
74 | // Add the outputs | 77 | // Add the outputs |
75 | &OutputTransformer{Module: b.Module}, | 78 | &OutputTransformer{Module: b.Module}, |
76 | 79 | ||
@@ -81,6 +84,12 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { | |||
81 | Module: b.Module, | 84 | Module: b.Module, |
82 | }, | 85 | }, |
83 | 86 | ||
87 | // Create orphan output nodes | ||
88 | &OrphanOutputTransformer{ | ||
89 | Module: b.Module, | ||
90 | State: b.State, | ||
91 | }, | ||
92 | |||
84 | // Attach the configuration to any resources | 93 | // Attach the configuration to any resources |
85 | &AttachResourceConfigTransformer{Module: b.Module}, | 94 | &AttachResourceConfigTransformer{Module: b.Module}, |
86 | 95 | ||
@@ -90,12 +99,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { | |||
90 | // Add root variables | 99 | // Add root variables |
91 | &RootVariableTransformer{Module: b.Module}, | 100 | &RootVariableTransformer{Module: b.Module}, |
92 | 101 | ||
93 | // Create all the providers | 102 | TransformProviders(b.Providers, b.ConcreteProvider, b.Module), |
94 | &MissingProviderTransformer{Providers: b.Providers, Concrete: b.ConcreteProvider}, | ||
95 | &ProviderTransformer{}, | ||
96 | &DisableProviderTransformer{}, | ||
97 | &ParentProviderTransformer{}, | ||
98 | &AttachProviderConfigTransformer{Module: b.Module}, | ||
99 | 103 | ||
100 | // Provisioner-related transformations. Only add these if requested. | 104 | // Provisioner-related transformations. Only add these if requested. |
101 | GraphTransformIf( | 105 | GraphTransformIf( |
@@ -107,7 +111,12 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { | |||
107 | ), | 111 | ), |
108 | 112 | ||
109 | // Add module variables | 113 | // Add module variables |
110 | &ModuleVariableTransformer{Module: b.Module}, | 114 | &ModuleVariableTransformer{ |
115 | Module: b.Module, | ||
116 | }, | ||
117 | |||
118 | // Remove modules no longer present in the config | ||
119 | &RemovedModuleTransformer{Module: b.Module, State: b.State}, | ||
111 | 120 | ||
112 | // Connect so that the references are ready for targeting. We'll | 121 | // Connect so that the references are ready for targeting. We'll |
113 | // have to connect again later for providers and so on. | 122 | // have to connect again later for providers and so on. |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go index 3d3e968..9638d4c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_builder_refresh.go | |||
@@ -126,12 +126,10 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer { | |||
126 | // Add root variables | 126 | // Add root variables |
127 | &RootVariableTransformer{Module: b.Module}, | 127 | &RootVariableTransformer{Module: b.Module}, |
128 | 128 | ||
129 | // Create all the providers | 129 | TransformProviders(b.Providers, concreteProvider, b.Module), |
130 | &MissingProviderTransformer{Providers: b.Providers, Concrete: concreteProvider}, | 130 | |
131 | &ProviderTransformer{}, | 131 | // Add the local values |
132 | &DisableProviderTransformer{}, | 132 | &LocalTransformer{Module: b.Module}, |
133 | &ParentProviderTransformer{}, | ||
134 | &AttachProviderConfigTransformer{Module: b.Module}, | ||
135 | 133 | ||
136 | // Add the outputs | 134 | // Add the outputs |
137 | &OutputTransformer{Module: b.Module}, | 135 | &OutputTransformer{Module: b.Module}, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go b/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go index e63b460..89f376e 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graph_walk_context.go | |||
@@ -32,7 +32,6 @@ type ContextGraphWalker struct { | |||
32 | interpolaterVars map[string]map[string]interface{} | 32 | interpolaterVars map[string]map[string]interface{} |
33 | interpolaterVarLock sync.Mutex | 33 | interpolaterVarLock sync.Mutex |
34 | providerCache map[string]ResourceProvider | 34 | providerCache map[string]ResourceProvider |
35 | providerConfigCache map[string]*ResourceConfig | ||
36 | providerLock sync.Mutex | 35 | providerLock sync.Mutex |
37 | provisionerCache map[string]ResourceProvisioner | 36 | provisionerCache map[string]ResourceProvisioner |
38 | provisionerLock sync.Mutex | 37 | provisionerLock sync.Mutex |
@@ -73,7 +72,6 @@ func (w *ContextGraphWalker) EnterPath(path []string) EvalContext { | |||
73 | InputValue: w.Context.uiInput, | 72 | InputValue: w.Context.uiInput, |
74 | Components: w.Context.components, | 73 | Components: w.Context.components, |
75 | ProviderCache: w.providerCache, | 74 | ProviderCache: w.providerCache, |
76 | ProviderConfigCache: w.providerConfigCache, | ||
77 | ProviderInputConfig: w.Context.providerInputConfig, | 75 | ProviderInputConfig: w.Context.providerInputConfig, |
78 | ProviderLock: &w.providerLock, | 76 | ProviderLock: &w.providerLock, |
79 | ProvisionerCache: w.provisionerCache, | 77 | ProvisionerCache: w.provisionerCache, |
@@ -151,7 +149,6 @@ func (w *ContextGraphWalker) ExitEvalTree( | |||
151 | func (w *ContextGraphWalker) init() { | 149 | func (w *ContextGraphWalker) init() { |
152 | w.contexts = make(map[string]*BuiltinEvalContext, 5) | 150 | w.contexts = make(map[string]*BuiltinEvalContext, 5) |
153 | w.providerCache = make(map[string]ResourceProvider, 5) | 151 | w.providerCache = make(map[string]ResourceProvider, 5) |
154 | w.providerConfigCache = make(map[string]*ResourceConfig, 5) | ||
155 | w.provisionerCache = make(map[string]ResourceProvisioner, 5) | 152 | w.provisionerCache = make(map[string]ResourceProvisioner, 5) |
156 | w.interpolaterVars = make(map[string]map[string]interface{}, 5) | 153 | w.interpolaterVars = make(map[string]map[string]interface{}, 5) |
157 | } | 154 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go b/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go index e97b485..95ef4e9 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go +++ b/vendor/github.com/hashicorp/terraform/terraform/graphtype_string.go | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | package terraform | 3 | package terraform |
4 | 4 | ||
5 | import "fmt" | 5 | import "strconv" |
6 | 6 | ||
7 | const _GraphType_name = "GraphTypeInvalidGraphTypeLegacyGraphTypeRefreshGraphTypePlanGraphTypePlanDestroyGraphTypeApplyGraphTypeInputGraphTypeValidate" | 7 | const _GraphType_name = "GraphTypeInvalidGraphTypeLegacyGraphTypeRefreshGraphTypePlanGraphTypePlanDestroyGraphTypeApplyGraphTypeInputGraphTypeValidate" |
8 | 8 | ||
@@ -10,7 +10,7 @@ var _GraphType_index = [...]uint8{0, 16, 31, 47, 60, 80, 94, 108, 125} | |||
10 | 10 | ||
11 | func (i GraphType) String() string { | 11 | func (i GraphType) String() string { |
12 | if i >= GraphType(len(_GraphType_index)-1) { | 12 | if i >= GraphType(len(_GraphType_index)-1) { |
13 | return fmt.Sprintf("GraphType(%d)", i) | 13 | return "GraphType(" + strconv.FormatInt(int64(i), 10) + ")" |
14 | } | 14 | } |
15 | return _GraphType_name[_GraphType_index[i]:_GraphType_index[i+1]] | 15 | return _GraphType_name[_GraphType_index[i]:_GraphType_index[i+1]] |
16 | } | 16 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go b/vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go index f69267c..b8e7d1f 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go +++ b/vendor/github.com/hashicorp/terraform/terraform/instancetype_string.go | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | package terraform | 3 | package terraform |
4 | 4 | ||
5 | import "fmt" | 5 | import "strconv" |
6 | 6 | ||
7 | const _InstanceType_name = "TypeInvalidTypePrimaryTypeTaintedTypeDeposed" | 7 | const _InstanceType_name = "TypeInvalidTypePrimaryTypeTaintedTypeDeposed" |
8 | 8 | ||
@@ -10,7 +10,7 @@ var _InstanceType_index = [...]uint8{0, 11, 22, 33, 44} | |||
10 | 10 | ||
11 | func (i InstanceType) String() string { | 11 | func (i InstanceType) String() string { |
12 | if i < 0 || i >= InstanceType(len(_InstanceType_index)-1) { | 12 | if i < 0 || i >= InstanceType(len(_InstanceType_index)-1) { |
13 | return fmt.Sprintf("InstanceType(%d)", i) | 13 | return "InstanceType(" + strconv.FormatInt(int64(i), 10) + ")" |
14 | } | 14 | } |
15 | return _InstanceType_name[_InstanceType_index[i]:_InstanceType_index[i+1]] | 15 | return _InstanceType_name[_InstanceType_index[i]:_InstanceType_index[i+1]] |
16 | } | 16 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/interpolate.go b/vendor/github.com/hashicorp/terraform/terraform/interpolate.go index 22ddce6..4f4e178 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/interpolate.go +++ b/vendor/github.com/hashicorp/terraform/terraform/interpolate.go | |||
@@ -90,6 +90,8 @@ func (i *Interpolater) Values( | |||
90 | err = i.valueSimpleVar(scope, n, v, result) | 90 | err = i.valueSimpleVar(scope, n, v, result) |
91 | case *config.TerraformVariable: | 91 | case *config.TerraformVariable: |
92 | err = i.valueTerraformVar(scope, n, v, result) | 92 | err = i.valueTerraformVar(scope, n, v, result) |
93 | case *config.LocalVariable: | ||
94 | err = i.valueLocalVar(scope, n, v, result) | ||
93 | case *config.UserVariable: | 95 | case *config.UserVariable: |
94 | err = i.valueUserVar(scope, n, v, result) | 96 | err = i.valueUserVar(scope, n, v, result) |
95 | default: | 97 | default: |
@@ -140,7 +142,6 @@ func (i *Interpolater) valueModuleVar( | |||
140 | n string, | 142 | n string, |
141 | v *config.ModuleVariable, | 143 | v *config.ModuleVariable, |
142 | result map[string]ast.Variable) error { | 144 | result map[string]ast.Variable) error { |
143 | |||
144 | // Build the path to the child module we want | 145 | // Build the path to the child module we want |
145 | path := make([]string, len(scope.Path), len(scope.Path)+1) | 146 | path := make([]string, len(scope.Path), len(scope.Path)+1) |
146 | copy(path, scope.Path) | 147 | copy(path, scope.Path) |
@@ -317,7 +318,6 @@ func (i *Interpolater) valueTerraformVar( | |||
317 | n string, | 318 | n string, |
318 | v *config.TerraformVariable, | 319 | v *config.TerraformVariable, |
319 | result map[string]ast.Variable) error { | 320 | result map[string]ast.Variable) error { |
320 | |||
321 | // "env" is supported for backward compatibility, but it's deprecated and | 321 | // "env" is supported for backward compatibility, but it's deprecated and |
322 | // so we won't advertise it as being allowed in the error message. It will | 322 | // so we won't advertise it as being allowed in the error message. It will |
323 | // be removed in a future version of Terraform. | 323 | // be removed in a future version of Terraform. |
@@ -335,6 +335,59 @@ func (i *Interpolater) valueTerraformVar( | |||
335 | return nil | 335 | return nil |
336 | } | 336 | } |
337 | 337 | ||
338 | func (i *Interpolater) valueLocalVar( | ||
339 | scope *InterpolationScope, | ||
340 | n string, | ||
341 | v *config.LocalVariable, | ||
342 | result map[string]ast.Variable, | ||
343 | ) error { | ||
344 | i.StateLock.RLock() | ||
345 | defer i.StateLock.RUnlock() | ||
346 | |||
347 | modTree := i.Module | ||
348 | if len(scope.Path) > 1 { | ||
349 | modTree = i.Module.Child(scope.Path[1:]) | ||
350 | } | ||
351 | |||
352 | // Get the resource from the configuration so we can verify | ||
353 | // that the resource is in the configuration and so we can access | ||
354 | // the configuration if we need to. | ||
355 | var cl *config.Local | ||
356 | for _, l := range modTree.Config().Locals { | ||
357 | if l.Name == v.Name { | ||
358 | cl = l | ||
359 | break | ||
360 | } | ||
361 | } | ||
362 | |||
363 | if cl == nil { | ||
364 | return fmt.Errorf("%s: no local value of this name has been declared", n) | ||
365 | } | ||
366 | |||
367 | // Get the relevant module | ||
368 | module := i.State.ModuleByPath(scope.Path) | ||
369 | if module == nil { | ||
370 | result[n] = unknownVariable() | ||
371 | return nil | ||
372 | } | ||
373 | |||
374 | rawV, exists := module.Locals[v.Name] | ||
375 | if !exists { | ||
376 | result[n] = unknownVariable() | ||
377 | return nil | ||
378 | } | ||
379 | |||
380 | varV, err := hil.InterfaceToVariable(rawV) | ||
381 | if err != nil { | ||
382 | // Should never happen, since interpolation should always produce | ||
383 | // something we can feed back in to interpolation. | ||
384 | return fmt.Errorf("%s: %s", n, err) | ||
385 | } | ||
386 | |||
387 | result[n] = varV | ||
388 | return nil | ||
389 | } | ||
390 | |||
338 | func (i *Interpolater) valueUserVar( | 391 | func (i *Interpolater) valueUserVar( |
339 | scope *InterpolationScope, | 392 | scope *InterpolationScope, |
340 | n string, | 393 | n string, |
@@ -465,6 +518,16 @@ func (i *Interpolater) computeResourceVariable( | |||
465 | return &v, err | 518 | return &v, err |
466 | } | 519 | } |
467 | 520 | ||
521 | // special case for the "id" field which is usually also an attribute | ||
522 | if v.Field == "id" && r.Primary.ID != "" { | ||
523 | // This is usually pulled from the attributes, but is sometimes missing | ||
524 | // during destroy. We can return the ID field in this case. | ||
525 | // FIXME: there should only be one ID to rule them all. | ||
526 | log.Printf("[WARN] resource %s missing 'id' attribute", v.ResourceId()) | ||
527 | v, err := hil.InterfaceToVariable(r.Primary.ID) | ||
528 | return &v, err | ||
529 | } | ||
530 | |||
468 | // computed list or map attribute | 531 | // computed list or map attribute |
469 | _, isList = r.Primary.Attributes[v.Field+".#"] | 532 | _, isList = r.Primary.Attributes[v.Field+".#"] |
470 | _, isMap = r.Primary.Attributes[v.Field+".%"] | 533 | _, isMap = r.Primary.Attributes[v.Field+".%"] |
@@ -602,6 +665,11 @@ func (i *Interpolater) computeResourceMultiVariable( | |||
602 | continue | 665 | continue |
603 | } | 666 | } |
604 | 667 | ||
668 | if v.Field == "id" && r.Primary.ID != "" { | ||
669 | log.Printf("[WARN] resource %s missing 'id' attribute", v.ResourceId()) | ||
670 | values = append(values, r.Primary.ID) | ||
671 | } | ||
672 | |||
605 | // computed list or map attribute | 673 | // computed list or map attribute |
606 | _, isList := r.Primary.Attributes[v.Field+".#"] | 674 | _, isList := r.Primary.Attributes[v.Field+".#"] |
607 | _, isMap := r.Primary.Attributes[v.Field+".%"] | 675 | _, isMap := r.Primary.Attributes[v.Field+".%"] |
@@ -646,7 +714,6 @@ func (i *Interpolater) computeResourceMultiVariable( | |||
646 | func (i *Interpolater) interpolateComplexTypeAttribute( | 714 | func (i *Interpolater) interpolateComplexTypeAttribute( |
647 | resourceID string, | 715 | resourceID string, |
648 | attributes map[string]string) (ast.Variable, error) { | 716 | attributes map[string]string) (ast.Variable, error) { |
649 | |||
650 | // We can now distinguish between lists and maps in state by the count field: | 717 | // We can now distinguish between lists and maps in state by the count field: |
651 | // - lists (and by extension, sets) use the traditional .# notation | 718 | // - lists (and by extension, sets) use the traditional .# notation |
652 | // - maps use the newer .% notation | 719 | // - maps use the newer .% notation |
@@ -722,7 +789,8 @@ func (i *Interpolater) resourceCountMax( | |||
722 | // If we're NOT applying, then we assume we can read the count | 789 | // If we're NOT applying, then we assume we can read the count |
723 | // from the state. Plan and so on may not have any state yet so | 790 | // from the state. Plan and so on may not have any state yet so |
724 | // we do a full interpolation. | 791 | // we do a full interpolation. |
725 | if i.Operation != walkApply { | 792 | // Don't forget walkDestroy, which is a special case of walkApply |
793 | if !(i.Operation == walkApply || i.Operation == walkDestroy) { | ||
726 | if cr == nil { | 794 | if cr == nil { |
727 | return 0, nil | 795 | return 0, nil |
728 | } | 796 | } |
@@ -753,7 +821,13 @@ func (i *Interpolater) resourceCountMax( | |||
753 | // use "cr.Count()" but that doesn't work if the count is interpolated | 821 | // use "cr.Count()" but that doesn't work if the count is interpolated |
754 | // and we can't guarantee that so we instead depend on the state. | 822 | // and we can't guarantee that so we instead depend on the state. |
755 | max := -1 | 823 | max := -1 |
756 | for k, _ := range ms.Resources { | 824 | for k, s := range ms.Resources { |
825 | // This resource may have been just removed, in which case the Primary | ||
826 | // may be nil, or just empty. | ||
827 | if s == nil || s.Primary == nil || len(s.Primary.Attributes) == 0 { | ||
828 | continue | ||
829 | } | ||
830 | |||
757 | // Get the index number for this resource | 831 | // Get the index number for this resource |
758 | index := "" | 832 | index := "" |
759 | if k == id { | 833 | if k == id { |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go b/vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go index b9f44a0..4594cb6 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go +++ b/vendor/github.com/hashicorp/terraform/terraform/module_dependencies.go | |||
@@ -17,7 +17,6 @@ import ( | |||
17 | // present in the configuration. This is guaranteed not to happen for any | 17 | // present in the configuration. This is guaranteed not to happen for any |
18 | // configuration that has passed a call to Config.Validate(). | 18 | // configuration that has passed a call to Config.Validate(). |
19 | func ModuleTreeDependencies(root *module.Tree, state *State) *moduledeps.Module { | 19 | func ModuleTreeDependencies(root *module.Tree, state *State) *moduledeps.Module { |
20 | |||
21 | // First we walk the configuration tree to build the overall structure | 20 | // First we walk the configuration tree to build the overall structure |
22 | // and capture the explicit/implicit/inherited provider dependencies. | 21 | // and capture the explicit/implicit/inherited provider dependencies. |
23 | deps := moduleTreeConfigDependencies(root, nil) | 22 | deps := moduleTreeConfigDependencies(root, nil) |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go b/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go index 45129b3..d5ca641 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_data_refresh.go | |||
@@ -27,6 +27,7 @@ func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, er | |||
27 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { | 27 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { |
28 | // Add the config and state since we don't do that via transforms | 28 | // Add the config and state since we don't do that via transforms |
29 | a.Config = n.Config | 29 | a.Config = n.Config |
30 | a.ResolvedProvider = n.ResolvedProvider | ||
30 | 31 | ||
31 | return &NodeRefreshableDataResourceInstance{ | 32 | return &NodeRefreshableDataResourceInstance{ |
32 | NodeAbstractResource: a, | 33 | NodeAbstractResource: a, |
@@ -107,7 +108,9 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode { | |||
107 | // Get the state if we have it, if not we build it | 108 | // Get the state if we have it, if not we build it |
108 | rs := n.ResourceState | 109 | rs := n.ResourceState |
109 | if rs == nil { | 110 | if rs == nil { |
110 | rs = &ResourceState{} | 111 | rs = &ResourceState{ |
112 | Provider: n.ResolvedProvider, | ||
113 | } | ||
111 | } | 114 | } |
112 | 115 | ||
113 | // If the config isn't empty we update the state | 116 | // If the config isn't empty we update the state |
@@ -145,7 +148,7 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode { | |||
145 | &EvalWriteState{ | 148 | &EvalWriteState{ |
146 | Name: stateId, | 149 | Name: stateId, |
147 | ResourceType: rs.Type, | 150 | ResourceType: rs.Type, |
148 | Provider: rs.Provider, | 151 | Provider: n.ResolvedProvider, |
149 | Dependencies: rs.Dependencies, | 152 | Dependencies: rs.Dependencies, |
150 | State: &state, // state is nil here | 153 | State: &state, // state is nil here |
151 | }, | 154 | }, |
@@ -185,7 +188,7 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode { | |||
185 | // provider configurations that need this data during | 188 | // provider configurations that need this data during |
186 | // refresh/plan. | 189 | // refresh/plan. |
187 | &EvalGetProvider{ | 190 | &EvalGetProvider{ |
188 | Name: n.ProvidedBy()[0], | 191 | Name: n.ResolvedProvider, |
189 | Output: &provider, | 192 | Output: &provider, |
190 | }, | 193 | }, |
191 | 194 | ||
@@ -207,7 +210,7 @@ func (n *NodeRefreshableDataResourceInstance) EvalTree() EvalNode { | |||
207 | &EvalWriteState{ | 210 | &EvalWriteState{ |
208 | Name: stateId, | 211 | Name: stateId, |
209 | ResourceType: rs.Type, | 212 | ResourceType: rs.Type, |
210 | Provider: rs.Provider, | 213 | Provider: n.ResolvedProvider, |
211 | Dependencies: rs.Dependencies, | 214 | Dependencies: rs.Dependencies, |
212 | State: &state, | 215 | State: &state, |
213 | }, | 216 | }, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_local.go b/vendor/github.com/hashicorp/terraform/terraform/node_local.go new file mode 100644 index 0000000..d387222 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/node_local.go | |||
@@ -0,0 +1,66 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strings" | ||
6 | |||
7 | "github.com/hashicorp/terraform/config" | ||
8 | ) | ||
9 | |||
10 | // NodeLocal represents a named local value in a particular module. | ||
11 | // | ||
12 | // Local value nodes only have one operation, common to all walk types: | ||
13 | // evaluate the result and place it in state. | ||
14 | type NodeLocal struct { | ||
15 | PathValue []string | ||
16 | Config *config.Local | ||
17 | } | ||
18 | |||
19 | func (n *NodeLocal) Name() string { | ||
20 | result := fmt.Sprintf("local.%s", n.Config.Name) | ||
21 | if len(n.PathValue) > 1 { | ||
22 | result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result) | ||
23 | } | ||
24 | |||
25 | return result | ||
26 | } | ||
27 | |||
28 | // GraphNodeSubPath | ||
29 | func (n *NodeLocal) Path() []string { | ||
30 | return n.PathValue | ||
31 | } | ||
32 | |||
33 | // RemovableIfNotTargeted | ||
34 | func (n *NodeLocal) RemoveIfNotTargeted() bool { | ||
35 | return true | ||
36 | } | ||
37 | |||
38 | // GraphNodeReferenceable | ||
39 | func (n *NodeLocal) ReferenceableName() []string { | ||
40 | name := fmt.Sprintf("local.%s", n.Config.Name) | ||
41 | return []string{name} | ||
42 | } | ||
43 | |||
44 | // GraphNodeReferencer | ||
45 | func (n *NodeLocal) References() []string { | ||
46 | var result []string | ||
47 | result = append(result, ReferencesFromConfig(n.Config.RawConfig)...) | ||
48 | for _, v := range result { | ||
49 | split := strings.Split(v, "/") | ||
50 | for i, s := range split { | ||
51 | split[i] = s + ".destroy" | ||
52 | } | ||
53 | |||
54 | result = append(result, strings.Join(split, "/")) | ||
55 | } | ||
56 | |||
57 | return result | ||
58 | } | ||
59 | |||
60 | // GraphNodeEvalable | ||
61 | func (n *NodeLocal) EvalTree() EvalNode { | ||
62 | return &EvalLocal{ | ||
63 | Name: n.Config.Name, | ||
64 | Value: n.Config.RawConfig, | ||
65 | } | ||
66 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_module_destroy.go b/vendor/github.com/hashicorp/terraform/terraform/node_module_destroy.go deleted file mode 100644 index 319df1e..0000000 --- a/vendor/github.com/hashicorp/terraform/terraform/node_module_destroy.go +++ /dev/null | |||
@@ -1,29 +0,0 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | ) | ||
6 | |||
7 | // NodeDestroyableModule represents a module destruction. | ||
8 | type NodeDestroyableModuleVariable struct { | ||
9 | PathValue []string | ||
10 | } | ||
11 | |||
12 | func (n *NodeDestroyableModuleVariable) Name() string { | ||
13 | result := "plan-destroy" | ||
14 | if len(n.PathValue) > 1 { | ||
15 | result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result) | ||
16 | } | ||
17 | |||
18 | return result | ||
19 | } | ||
20 | |||
21 | // GraphNodeSubPath | ||
22 | func (n *NodeDestroyableModuleVariable) Path() []string { | ||
23 | return n.PathValue | ||
24 | } | ||
25 | |||
26 | // GraphNodeEvalable | ||
27 | func (n *NodeDestroyableModuleVariable) EvalTree() EvalNode { | ||
28 | return &EvalDiffDestroyModule{Path: n.PathValue} | ||
29 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_module_removed.go b/vendor/github.com/hashicorp/terraform/terraform/node_module_removed.go new file mode 100644 index 0000000..bb3e5ee --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/node_module_removed.go | |||
@@ -0,0 +1,77 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "log" | ||
6 | "reflect" | ||
7 | ) | ||
8 | |||
9 | // NodeModuleRemoved represents a module that is no longer in the | ||
10 | // config. | ||
11 | type NodeModuleRemoved struct { | ||
12 | PathValue []string | ||
13 | } | ||
14 | |||
15 | func (n *NodeModuleRemoved) Name() string { | ||
16 | return fmt.Sprintf("%s (removed)", modulePrefixStr(n.PathValue)) | ||
17 | } | ||
18 | |||
19 | // GraphNodeSubPath | ||
20 | func (n *NodeModuleRemoved) Path() []string { | ||
21 | return n.PathValue | ||
22 | } | ||
23 | |||
24 | // GraphNodeEvalable | ||
25 | func (n *NodeModuleRemoved) EvalTree() EvalNode { | ||
26 | return &EvalOpFilter{ | ||
27 | Ops: []walkOperation{walkRefresh, walkApply, walkDestroy}, | ||
28 | Node: &EvalDeleteModule{ | ||
29 | PathValue: n.PathValue, | ||
30 | }, | ||
31 | } | ||
32 | } | ||
33 | |||
34 | func (n *NodeModuleRemoved) ReferenceGlobal() bool { | ||
35 | return true | ||
36 | } | ||
37 | |||
38 | func (n *NodeModuleRemoved) References() []string { | ||
39 | return []string{modulePrefixStr(n.PathValue)} | ||
40 | } | ||
41 | |||
42 | // EvalDeleteModule is an EvalNode implementation that removes an empty module | ||
43 | // entry from the state. | ||
44 | type EvalDeleteModule struct { | ||
45 | PathValue []string | ||
46 | } | ||
47 | |||
48 | func (n *EvalDeleteModule) Eval(ctx EvalContext) (interface{}, error) { | ||
49 | state, lock := ctx.State() | ||
50 | if state == nil { | ||
51 | return nil, nil | ||
52 | } | ||
53 | |||
54 | // Get a write lock so we can access this instance | ||
55 | lock.Lock() | ||
56 | defer lock.Unlock() | ||
57 | |||
58 | // Make sure we have a clean state | ||
59 | // Destroyed resources aren't deleted, they're written with an ID of "". | ||
60 | state.prune() | ||
61 | |||
62 | // find the module and delete it | ||
63 | for i, m := range state.Modules { | ||
64 | if reflect.DeepEqual(m.Path, n.PathValue) { | ||
65 | if !m.Empty() { | ||
66 | // a targeted apply may leave module resources even without a config, | ||
67 | // so just log this and return. | ||
68 | log.Printf("[DEBUG] cannot remove module %s, not empty", modulePrefixStr(n.PathValue)) | ||
69 | break | ||
70 | } | ||
71 | state.Modules = append(state.Modules[:i], state.Modules[i+1:]...) | ||
72 | break | ||
73 | } | ||
74 | } | ||
75 | |||
76 | return nil, nil | ||
77 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go b/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go index 13fe8fc..66ff7d5 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_module_variable.go | |||
@@ -92,11 +92,24 @@ func (n *NodeApplyableModuleVariable) EvalTree() EvalNode { | |||
92 | // within the variables mapping. | 92 | // within the variables mapping. |
93 | var config *ResourceConfig | 93 | var config *ResourceConfig |
94 | variables := make(map[string]interface{}) | 94 | variables := make(map[string]interface{}) |
95 | |||
95 | return &EvalSequence{ | 96 | return &EvalSequence{ |
96 | Nodes: []EvalNode{ | 97 | Nodes: []EvalNode{ |
97 | &EvalInterpolate{ | 98 | &EvalOpFilter{ |
98 | Config: n.Value, | 99 | Ops: []walkOperation{walkInput}, |
99 | Output: &config, | 100 | Node: &EvalInterpolate{ |
101 | Config: n.Value, | ||
102 | Output: &config, | ||
103 | ContinueOnErr: true, | ||
104 | }, | ||
105 | }, | ||
106 | &EvalOpFilter{ | ||
107 | Ops: []walkOperation{walkRefresh, walkPlan, walkApply, | ||
108 | walkDestroy, walkValidate}, | ||
109 | Node: &EvalInterpolate{ | ||
110 | Config: n.Value, | ||
111 | Output: &config, | ||
112 | }, | ||
100 | }, | 113 | }, |
101 | 114 | ||
102 | &EvalVariableBlock{ | 115 | &EvalVariableBlock{ |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_output.go b/vendor/github.com/hashicorp/terraform/terraform/node_output.go index 9017a63..83e9925 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_output.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_output.go | |||
@@ -69,12 +69,22 @@ func (n *NodeApplyableOutput) References() []string { | |||
69 | 69 | ||
70 | // GraphNodeEvalable | 70 | // GraphNodeEvalable |
71 | func (n *NodeApplyableOutput) EvalTree() EvalNode { | 71 | func (n *NodeApplyableOutput) EvalTree() EvalNode { |
72 | return &EvalOpFilter{ | 72 | return &EvalSequence{ |
73 | Ops: []walkOperation{walkRefresh, walkPlan, walkApply, | 73 | Nodes: []EvalNode{ |
74 | walkDestroy, walkInput, walkValidate}, | 74 | &EvalOpFilter{ |
75 | Node: &EvalSequence{ | 75 | // Don't let interpolation errors stop Input, since it happens |
76 | Nodes: []EvalNode{ | 76 | // before Refresh. |
77 | &EvalWriteOutput{ | 77 | Ops: []walkOperation{walkInput}, |
78 | Node: &EvalWriteOutput{ | ||
79 | Name: n.Config.Name, | ||
80 | Sensitive: n.Config.Sensitive, | ||
81 | Value: n.Config.RawConfig, | ||
82 | ContinueOnErr: true, | ||
83 | }, | ||
84 | }, | ||
85 | &EvalOpFilter{ | ||
86 | Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy}, | ||
87 | Node: &EvalWriteOutput{ | ||
78 | Name: n.Config.Name, | 88 | Name: n.Config.Name, |
79 | Sensitive: n.Config.Sensitive, | 89 | Sensitive: n.Config.Sensitive, |
80 | Value: n.Config.RawConfig, | 90 | Value: n.Config.RawConfig, |
@@ -83,3 +93,61 @@ func (n *NodeApplyableOutput) EvalTree() EvalNode { | |||
83 | }, | 93 | }, |
84 | } | 94 | } |
85 | } | 95 | } |
96 | |||
97 | // NodeDestroyableOutput represents an output that is "destroybale": | ||
98 | // its application will remove the output from the state. | ||
99 | type NodeDestroyableOutput struct { | ||
100 | PathValue []string | ||
101 | Config *config.Output // Config is the output in the config | ||
102 | } | ||
103 | |||
104 | func (n *NodeDestroyableOutput) Name() string { | ||
105 | result := fmt.Sprintf("output.%s (destroy)", n.Config.Name) | ||
106 | if len(n.PathValue) > 1 { | ||
107 | result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result) | ||
108 | } | ||
109 | |||
110 | return result | ||
111 | } | ||
112 | |||
113 | // GraphNodeSubPath | ||
114 | func (n *NodeDestroyableOutput) Path() []string { | ||
115 | return n.PathValue | ||
116 | } | ||
117 | |||
118 | // RemovableIfNotTargeted | ||
119 | func (n *NodeDestroyableOutput) RemoveIfNotTargeted() bool { | ||
120 | // We need to add this so that this node will be removed if | ||
121 | // it isn't targeted or a dependency of a target. | ||
122 | return true | ||
123 | } | ||
124 | |||
125 | // This will keep the destroy node in the graph if its corresponding output | ||
126 | // node is also in the destroy graph. | ||
127 | func (n *NodeDestroyableOutput) TargetDownstream(targetedDeps, untargetedDeps *dag.Set) bool { | ||
128 | return true | ||
129 | } | ||
130 | |||
131 | // GraphNodeReferencer | ||
132 | func (n *NodeDestroyableOutput) References() []string { | ||
133 | var result []string | ||
134 | result = append(result, n.Config.DependsOn...) | ||
135 | result = append(result, ReferencesFromConfig(n.Config.RawConfig)...) | ||
136 | for _, v := range result { | ||
137 | split := strings.Split(v, "/") | ||
138 | for i, s := range split { | ||
139 | split[i] = s + ".destroy" | ||
140 | } | ||
141 | |||
142 | result = append(result, strings.Join(split, "/")) | ||
143 | } | ||
144 | |||
145 | return result | ||
146 | } | ||
147 | |||
148 | // GraphNodeEvalable | ||
149 | func (n *NodeDestroyableOutput) EvalTree() EvalNode { | ||
150 | return &EvalDeleteOutput{ | ||
151 | Name: n.Config.Name, | ||
152 | } | ||
153 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_output_orphan.go b/vendor/github.com/hashicorp/terraform/terraform/node_output_orphan.go index 636a15d..0fd1554 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_output_orphan.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_output_orphan.go | |||
@@ -19,6 +19,11 @@ func (n *NodeOutputOrphan) Name() string { | |||
19 | return result | 19 | return result |
20 | } | 20 | } |
21 | 21 | ||
22 | // GraphNodeReferenceable | ||
23 | func (n *NodeOutputOrphan) ReferenceableName() []string { | ||
24 | return []string{"output." + n.OutputName} | ||
25 | } | ||
26 | |||
22 | // GraphNodeSubPath | 27 | // GraphNodeSubPath |
23 | func (n *NodeOutputOrphan) Path() []string { | 28 | func (n *NodeOutputOrphan) Path() []string { |
24 | return n.PathValue | 29 | return n.PathValue |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provider.go b/vendor/github.com/hashicorp/terraform/terraform/node_provider.go index 8e2c176..2071ab1 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_provider.go | |||
@@ -7,5 +7,5 @@ type NodeApplyableProvider struct { | |||
7 | 7 | ||
8 | // GraphNodeEvalable | 8 | // GraphNodeEvalable |
9 | func (n *NodeApplyableProvider) EvalTree() EvalNode { | 9 | func (n *NodeApplyableProvider) EvalTree() EvalNode { |
10 | return ProviderEvalTree(n.NameValue, n.ProviderConfig()) | 10 | return ProviderEvalTree(n, n.ProviderConfig()) |
11 | } | 11 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provider_abstract.go b/vendor/github.com/hashicorp/terraform/terraform/node_provider_abstract.go index 6cc8365..9e490f7 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_provider_abstract.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_provider_abstract.go | |||
@@ -2,6 +2,7 @@ package terraform | |||
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "fmt" |
5 | "strings" | ||
5 | 6 | ||
6 | "github.com/hashicorp/terraform/config" | 7 | "github.com/hashicorp/terraform/config" |
7 | "github.com/hashicorp/terraform/dag" | 8 | "github.com/hashicorp/terraform/dag" |
@@ -24,13 +25,22 @@ type NodeAbstractProvider struct { | |||
24 | Config *config.ProviderConfig | 25 | Config *config.ProviderConfig |
25 | } | 26 | } |
26 | 27 | ||
27 | func (n *NodeAbstractProvider) Name() string { | 28 | func ResolveProviderName(name string, path []string) string { |
28 | result := fmt.Sprintf("provider.%s", n.NameValue) | 29 | if strings.Contains(name, "provider.") { |
29 | if len(n.PathValue) > 1 { | 30 | // already resolved |
30 | result = fmt.Sprintf("%s.%s", modulePrefixStr(n.PathValue), result) | 31 | return name |
32 | } | ||
33 | |||
34 | name = fmt.Sprintf("provider.%s", name) | ||
35 | if len(path) >= 1 { | ||
36 | name = fmt.Sprintf("%s.%s", modulePrefixStr(path), name) | ||
31 | } | 37 | } |
32 | 38 | ||
33 | return result | 39 | return name |
40 | } | ||
41 | |||
42 | func (n *NodeAbstractProvider) Name() string { | ||
43 | return ResolveProviderName(n.NameValue, n.PathValue) | ||
34 | } | 44 | } |
35 | 45 | ||
36 | // GraphNodeSubPath | 46 | // GraphNodeSubPath |
@@ -60,12 +70,12 @@ func (n *NodeAbstractProvider) ProviderName() string { | |||
60 | } | 70 | } |
61 | 71 | ||
62 | // GraphNodeProvider | 72 | // GraphNodeProvider |
63 | func (n *NodeAbstractProvider) ProviderConfig() *config.RawConfig { | 73 | func (n *NodeAbstractProvider) ProviderConfig() *config.ProviderConfig { |
64 | if n.Config == nil { | 74 | if n.Config == nil { |
65 | return nil | 75 | return nil |
66 | } | 76 | } |
67 | 77 | ||
68 | return n.Config.RawConfig | 78 | return n.Config |
69 | } | 79 | } |
70 | 80 | ||
71 | // GraphNodeAttachProvider | 81 | // GraphNodeAttachProvider |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_provider_disabled.go b/vendor/github.com/hashicorp/terraform/terraform/node_provider_disabled.go index 25e7e62..a00bc46 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_provider_disabled.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_provider_disabled.go | |||
@@ -20,7 +20,7 @@ func (n *NodeDisabledProvider) EvalTree() EvalNode { | |||
20 | var resourceConfig *ResourceConfig | 20 | var resourceConfig *ResourceConfig |
21 | return &EvalSequence{ | 21 | return &EvalSequence{ |
22 | Nodes: []EvalNode{ | 22 | Nodes: []EvalNode{ |
23 | &EvalInterpolate{ | 23 | &EvalInterpolateProvider{ |
24 | Config: n.ProviderConfig(), | 24 | Config: n.ProviderConfig(), |
25 | Output: &resourceConfig, | 25 | Output: &resourceConfig, |
26 | }, | 26 | }, |
@@ -29,10 +29,6 @@ func (n *NodeDisabledProvider) EvalTree() EvalNode { | |||
29 | Config: &resourceConfig, | 29 | Config: &resourceConfig, |
30 | Output: &resourceConfig, | 30 | Output: &resourceConfig, |
31 | }, | 31 | }, |
32 | &EvalSetProviderConfig{ | ||
33 | Provider: n.ProviderName(), | ||
34 | Config: &resourceConfig, | ||
35 | }, | ||
36 | }, | 32 | }, |
37 | } | 33 | } |
38 | } | 34 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go index 50bb707..73509c8 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_abstract.go | |||
@@ -33,6 +33,9 @@ type NodeAbstractResource struct { | |||
33 | ResourceState *ResourceState // ResourceState is the ResourceState for this | 33 | ResourceState *ResourceState // ResourceState is the ResourceState for this |
34 | 34 | ||
35 | Targets []ResourceAddress // Set from GraphNodeTargetable | 35 | Targets []ResourceAddress // Set from GraphNodeTargetable |
36 | |||
37 | // The address of the provider this resource will use | ||
38 | ResolvedProvider string | ||
36 | } | 39 | } |
37 | 40 | ||
38 | func (n *NodeAbstractResource) Name() string { | 41 | func (n *NodeAbstractResource) Name() string { |
@@ -170,20 +173,24 @@ func (n *NodeAbstractResource) StateReferences() []string { | |||
170 | return deps | 173 | return deps |
171 | } | 174 | } |
172 | 175 | ||
176 | func (n *NodeAbstractResource) SetProvider(p string) { | ||
177 | n.ResolvedProvider = p | ||
178 | } | ||
179 | |||
173 | // GraphNodeProviderConsumer | 180 | // GraphNodeProviderConsumer |
174 | func (n *NodeAbstractResource) ProvidedBy() []string { | 181 | func (n *NodeAbstractResource) ProvidedBy() string { |
175 | // If we have a config we prefer that above all else | 182 | // If we have a config we prefer that above all else |
176 | if n.Config != nil { | 183 | if n.Config != nil { |
177 | return []string{resourceProvider(n.Config.Type, n.Config.Provider)} | 184 | return resourceProvider(n.Config.Type, n.Config.Provider) |
178 | } | 185 | } |
179 | 186 | ||
180 | // If we have state, then we will use the provider from there | 187 | // If we have state, then we will use the provider from there |
181 | if n.ResourceState != nil && n.ResourceState.Provider != "" { | 188 | if n.ResourceState != nil && n.ResourceState.Provider != "" { |
182 | return []string{n.ResourceState.Provider} | 189 | return n.ResourceState.Provider |
183 | } | 190 | } |
184 | 191 | ||
185 | // Use our type | 192 | // Use our type |
186 | return []string{resourceProvider(n.Addr.Type, "")} | 193 | return resourceProvider(n.Addr.Type, "") |
187 | } | 194 | } |
188 | 195 | ||
189 | // GraphNodeProvisionerConsumer | 196 | // GraphNodeProvisionerConsumer |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go index 3599782..40ee1cf 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_apply.go | |||
@@ -124,6 +124,27 @@ func (n *NodeApplyableResource) evalTreeDataResource( | |||
124 | Then: EvalNoop{}, | 124 | Then: EvalNoop{}, |
125 | }, | 125 | }, |
126 | 126 | ||
127 | // Normally we interpolate count as a preparation step before | ||
128 | // a DynamicExpand, but an apply graph has pre-expanded nodes | ||
129 | // and so the count would otherwise never be interpolated. | ||
130 | // | ||
131 | // This is redundant when there are multiple instances created | ||
132 | // from the same config (count > 1) but harmless since the | ||
133 | // underlying structures have mutexes to make this concurrency-safe. | ||
134 | // | ||
135 | // In most cases this isn't actually needed because we dealt with | ||
136 | // all of the counts during the plan walk, but we do it here | ||
137 | // for completeness because other code assumes that the | ||
138 | // final count is always available during interpolation. | ||
139 | // | ||
140 | // Here we are just populating the interpolated value in-place | ||
141 | // inside this RawConfig object, like we would in | ||
142 | // NodeAbstractCountResource. | ||
143 | &EvalInterpolate{ | ||
144 | Config: n.Config.RawCount, | ||
145 | ContinueOnErr: true, | ||
146 | }, | ||
147 | |||
127 | // We need to re-interpolate the config here, rather than | 148 | // We need to re-interpolate the config here, rather than |
128 | // just using the diff's values directly, because we've | 149 | // just using the diff's values directly, because we've |
129 | // potentially learned more variable values during the | 150 | // potentially learned more variable values during the |
@@ -135,7 +156,7 @@ func (n *NodeApplyableResource) evalTreeDataResource( | |||
135 | }, | 156 | }, |
136 | 157 | ||
137 | &EvalGetProvider{ | 158 | &EvalGetProvider{ |
138 | Name: n.ProvidedBy()[0], | 159 | Name: n.ResolvedProvider, |
139 | Output: &provider, | 160 | Output: &provider, |
140 | }, | 161 | }, |
141 | 162 | ||
@@ -158,7 +179,7 @@ func (n *NodeApplyableResource) evalTreeDataResource( | |||
158 | &EvalWriteState{ | 179 | &EvalWriteState{ |
159 | Name: stateId, | 180 | Name: stateId, |
160 | ResourceType: n.Config.Type, | 181 | ResourceType: n.Config.Type, |
161 | Provider: n.Config.Provider, | 182 | Provider: n.ResolvedProvider, |
162 | Dependencies: stateDeps, | 183 | Dependencies: stateDeps, |
163 | State: &state, | 184 | State: &state, |
164 | }, | 185 | }, |
@@ -236,13 +257,35 @@ func (n *NodeApplyableResource) evalTreeManagedResource( | |||
236 | }, | 257 | }, |
237 | }, | 258 | }, |
238 | 259 | ||
260 | // Normally we interpolate count as a preparation step before | ||
261 | // a DynamicExpand, but an apply graph has pre-expanded nodes | ||
262 | // and so the count would otherwise never be interpolated. | ||
263 | // | ||
264 | // This is redundant when there are multiple instances created | ||
265 | // from the same config (count > 1) but harmless since the | ||
266 | // underlying structures have mutexes to make this concurrency-safe. | ||
267 | // | ||
268 | // In most cases this isn't actually needed because we dealt with | ||
269 | // all of the counts during the plan walk, but we need to do this | ||
270 | // in order to support interpolation of resource counts from | ||
271 | // apply-time-interpolated expressions, such as those in | ||
272 | // "provisioner" blocks. | ||
273 | // | ||
274 | // Here we are just populating the interpolated value in-place | ||
275 | // inside this RawConfig object, like we would in | ||
276 | // NodeAbstractCountResource. | ||
277 | &EvalInterpolate{ | ||
278 | Config: n.Config.RawCount, | ||
279 | ContinueOnErr: true, | ||
280 | }, | ||
281 | |||
239 | &EvalInterpolate{ | 282 | &EvalInterpolate{ |
240 | Config: n.Config.RawConfig.Copy(), | 283 | Config: n.Config.RawConfig.Copy(), |
241 | Resource: resource, | 284 | Resource: resource, |
242 | Output: &resourceConfig, | 285 | Output: &resourceConfig, |
243 | }, | 286 | }, |
244 | &EvalGetProvider{ | 287 | &EvalGetProvider{ |
245 | Name: n.ProvidedBy()[0], | 288 | Name: n.ResolvedProvider, |
246 | Output: &provider, | 289 | Output: &provider, |
247 | }, | 290 | }, |
248 | &EvalReadState{ | 291 | &EvalReadState{ |
@@ -283,7 +326,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource( | |||
283 | }, | 326 | }, |
284 | 327 | ||
285 | &EvalGetProvider{ | 328 | &EvalGetProvider{ |
286 | Name: n.ProvidedBy()[0], | 329 | Name: n.ResolvedProvider, |
287 | Output: &provider, | 330 | Output: &provider, |
288 | }, | 331 | }, |
289 | &EvalReadState{ | 332 | &EvalReadState{ |
@@ -308,7 +351,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource( | |||
308 | &EvalWriteState{ | 351 | &EvalWriteState{ |
309 | Name: stateId, | 352 | Name: stateId, |
310 | ResourceType: n.Config.Type, | 353 | ResourceType: n.Config.Type, |
311 | Provider: n.Config.Provider, | 354 | Provider: n.ResolvedProvider, |
312 | Dependencies: stateDeps, | 355 | Dependencies: stateDeps, |
313 | State: &state, | 356 | State: &state, |
314 | }, | 357 | }, |
@@ -332,7 +375,7 @@ func (n *NodeApplyableResource) evalTreeManagedResource( | |||
332 | Else: &EvalWriteState{ | 375 | Else: &EvalWriteState{ |
333 | Name: stateId, | 376 | Name: stateId, |
334 | ResourceType: n.Config.Type, | 377 | ResourceType: n.Config.Type, |
335 | Provider: n.Config.Provider, | 378 | Provider: n.ResolvedProvider, |
336 | Dependencies: stateDeps, | 379 | Dependencies: stateDeps, |
337 | State: &state, | 380 | State: &state, |
338 | }, | 381 | }, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go index c2efd2c..657bbee 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_destroy.go | |||
@@ -102,8 +102,9 @@ func (n *NodeDestroyResource) DynamicExpand(ctx EvalContext) (*Graph, error) { | |||
102 | 102 | ||
103 | // We want deposed resources in the state to be destroyed | 103 | // We want deposed resources in the state to be destroyed |
104 | steps = append(steps, &DeposedTransformer{ | 104 | steps = append(steps, &DeposedTransformer{ |
105 | State: state, | 105 | State: state, |
106 | View: n.Addr.stateId(), | 106 | View: n.Addr.stateId(), |
107 | ResolvedProvider: n.ResolvedProvider, | ||
107 | }) | 108 | }) |
108 | 109 | ||
109 | // Target | 110 | // Target |
@@ -148,7 +149,9 @@ func (n *NodeDestroyResource) EvalTree() EvalNode { | |||
148 | // Get our state | 149 | // Get our state |
149 | rs := n.ResourceState | 150 | rs := n.ResourceState |
150 | if rs == nil { | 151 | if rs == nil { |
151 | rs = &ResourceState{} | 152 | rs = &ResourceState{ |
153 | Provider: n.ResolvedProvider, | ||
154 | } | ||
152 | } | 155 | } |
153 | 156 | ||
154 | var diffApply *InstanceDiff | 157 | var diffApply *InstanceDiff |
@@ -188,7 +191,7 @@ func (n *NodeDestroyResource) EvalTree() EvalNode { | |||
188 | &EvalInstanceInfo{Info: info}, | 191 | &EvalInstanceInfo{Info: info}, |
189 | 192 | ||
190 | &EvalGetProvider{ | 193 | &EvalGetProvider{ |
191 | Name: n.ProvidedBy()[0], | 194 | Name: n.ResolvedProvider, |
192 | Output: &provider, | 195 | Output: &provider, |
193 | }, | 196 | }, |
194 | &EvalReadState{ | 197 | &EvalReadState{ |
@@ -272,7 +275,7 @@ func (n *NodeDestroyResource) EvalTree() EvalNode { | |||
272 | &EvalWriteState{ | 275 | &EvalWriteState{ |
273 | Name: stateId, | 276 | Name: stateId, |
274 | ResourceType: n.Addr.Type, | 277 | ResourceType: n.Addr.Type, |
275 | Provider: rs.Provider, | 278 | Provider: n.ResolvedProvider, |
276 | Dependencies: rs.Dependencies, | 279 | Dependencies: rs.Dependencies, |
277 | State: &state, | 280 | State: &state, |
278 | }, | 281 | }, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go index 52bbf88..1afae7a 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan.go | |||
@@ -27,6 +27,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { | |||
27 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { | 27 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { |
28 | // Add the config and state since we don't do that via transforms | 28 | // Add the config and state since we don't do that via transforms |
29 | a.Config = n.Config | 29 | a.Config = n.Config |
30 | a.ResolvedProvider = n.ResolvedProvider | ||
30 | 31 | ||
31 | return &NodePlannableResourceInstance{ | 32 | return &NodePlannableResourceInstance{ |
32 | NodeAbstractResource: a, | 33 | NodeAbstractResource: a, |
@@ -37,6 +38,7 @@ func (n *NodePlannableResource) DynamicExpand(ctx EvalContext) (*Graph, error) { | |||
37 | concreteResourceOrphan := func(a *NodeAbstractResource) dag.Vertex { | 38 | concreteResourceOrphan := func(a *NodeAbstractResource) dag.Vertex { |
38 | // Add the config and state since we don't do that via transforms | 39 | // Add the config and state since we don't do that via transforms |
39 | a.Config = n.Config | 40 | a.Config = n.Config |
41 | a.ResolvedProvider = n.ResolvedProvider | ||
40 | 42 | ||
41 | return &NodePlannableResourceOrphan{ | 43 | return &NodePlannableResourceOrphan{ |
42 | NodeAbstractResource: a, | 44 | NodeAbstractResource: a, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go index b529569..7d9fcdd 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_plan_instance.go | |||
@@ -97,7 +97,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource( | |||
97 | }, | 97 | }, |
98 | 98 | ||
99 | &EvalGetProvider{ | 99 | &EvalGetProvider{ |
100 | Name: n.ProvidedBy()[0], | 100 | Name: n.ResolvedProvider, |
101 | Output: &provider, | 101 | Output: &provider, |
102 | }, | 102 | }, |
103 | 103 | ||
@@ -112,7 +112,7 @@ func (n *NodePlannableResourceInstance) evalTreeDataResource( | |||
112 | &EvalWriteState{ | 112 | &EvalWriteState{ |
113 | Name: stateId, | 113 | Name: stateId, |
114 | ResourceType: n.Config.Type, | 114 | ResourceType: n.Config.Type, |
115 | Provider: n.Config.Provider, | 115 | Provider: n.ResolvedProvider, |
116 | Dependencies: stateDeps, | 116 | Dependencies: stateDeps, |
117 | State: &state, | 117 | State: &state, |
118 | }, | 118 | }, |
@@ -143,7 +143,7 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource( | |||
143 | Output: &resourceConfig, | 143 | Output: &resourceConfig, |
144 | }, | 144 | }, |
145 | &EvalGetProvider{ | 145 | &EvalGetProvider{ |
146 | Name: n.ProvidedBy()[0], | 146 | Name: n.ResolvedProvider, |
147 | Output: &provider, | 147 | Output: &provider, |
148 | }, | 148 | }, |
149 | // Re-run validation to catch any errors we missed, e.g. type | 149 | // Re-run validation to catch any errors we missed, e.g. type |
@@ -177,7 +177,7 @@ func (n *NodePlannableResourceInstance) evalTreeManagedResource( | |||
177 | &EvalWriteState{ | 177 | &EvalWriteState{ |
178 | Name: stateId, | 178 | Name: stateId, |
179 | ResourceType: n.Config.Type, | 179 | ResourceType: n.Config.Type, |
180 | Provider: n.Config.Provider, | 180 | Provider: n.ResolvedProvider, |
181 | Dependencies: stateDeps, | 181 | Dependencies: stateDeps, |
182 | State: &state, | 182 | State: &state, |
183 | }, | 183 | }, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go index cd4fe92..697bd49 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_refresh.go | |||
@@ -30,6 +30,7 @@ func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, | |||
30 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { | 30 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { |
31 | // Add the config and state since we don't do that via transforms | 31 | // Add the config and state since we don't do that via transforms |
32 | a.Config = n.Config | 32 | a.Config = n.Config |
33 | a.ResolvedProvider = n.ResolvedProvider | ||
33 | 34 | ||
34 | return &NodeRefreshableManagedResourceInstance{ | 35 | return &NodeRefreshableManagedResourceInstance{ |
35 | NodeAbstractResource: a, | 36 | NodeAbstractResource: a, |
@@ -149,7 +150,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN | |||
149 | return &EvalSequence{ | 150 | return &EvalSequence{ |
150 | Nodes: []EvalNode{ | 151 | Nodes: []EvalNode{ |
151 | &EvalGetProvider{ | 152 | &EvalGetProvider{ |
152 | Name: n.ProvidedBy()[0], | 153 | Name: n.ResolvedProvider, |
153 | Output: &provider, | 154 | Output: &provider, |
154 | }, | 155 | }, |
155 | &EvalReadState{ | 156 | &EvalReadState{ |
@@ -165,7 +166,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalN | |||
165 | &EvalWriteState{ | 166 | &EvalWriteState{ |
166 | Name: stateId, | 167 | Name: stateId, |
167 | ResourceType: n.ResourceState.Type, | 168 | ResourceType: n.ResourceState.Type, |
168 | Provider: n.ResourceState.Provider, | 169 | Provider: n.ResolvedProvider, |
169 | Dependencies: n.ResourceState.Dependencies, | 170 | Dependencies: n.ResourceState.Dependencies, |
170 | State: &state, | 171 | State: &state, |
171 | }, | 172 | }, |
@@ -212,15 +213,21 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState( | |||
212 | // Determine the dependencies for the state. | 213 | // Determine the dependencies for the state. |
213 | stateDeps := n.StateReferences() | 214 | stateDeps := n.StateReferences() |
214 | 215 | ||
216 | // n.Config can be nil if the config and state don't match | ||
217 | var raw *config.RawConfig | ||
218 | if n.Config != nil { | ||
219 | raw = n.Config.RawConfig.Copy() | ||
220 | } | ||
221 | |||
215 | return &EvalSequence{ | 222 | return &EvalSequence{ |
216 | Nodes: []EvalNode{ | 223 | Nodes: []EvalNode{ |
217 | &EvalInterpolate{ | 224 | &EvalInterpolate{ |
218 | Config: n.Config.RawConfig.Copy(), | 225 | Config: raw, |
219 | Resource: resource, | 226 | Resource: resource, |
220 | Output: &resourceConfig, | 227 | Output: &resourceConfig, |
221 | }, | 228 | }, |
222 | &EvalGetProvider{ | 229 | &EvalGetProvider{ |
223 | Name: n.ProvidedBy()[0], | 230 | Name: n.ResolvedProvider, |
224 | Output: &provider, | 231 | Output: &provider, |
225 | }, | 232 | }, |
226 | // Re-run validation to catch any errors we missed, e.g. type | 233 | // Re-run validation to catch any errors we missed, e.g. type |
@@ -250,7 +257,7 @@ func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState( | |||
250 | &EvalWriteState{ | 257 | &EvalWriteState{ |
251 | Name: stateID, | 258 | Name: stateID, |
252 | ResourceType: n.Config.Type, | 259 | ResourceType: n.Config.Type, |
253 | Provider: n.Config.Provider, | 260 | Provider: n.ResolvedProvider, |
254 | Dependencies: stateDeps, | 261 | Dependencies: stateDeps, |
255 | State: &state, | 262 | State: &state, |
256 | }, | 263 | }, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go b/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go index f528f24..0df223d 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go +++ b/vendor/github.com/hashicorp/terraform/terraform/node_resource_validate.go | |||
@@ -39,6 +39,7 @@ func (n *NodeValidatableResource) DynamicExpand(ctx EvalContext) (*Graph, error) | |||
39 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { | 39 | concreteResource := func(a *NodeAbstractResource) dag.Vertex { |
40 | // Add the config and state since we don't do that via transforms | 40 | // Add the config and state since we don't do that via transforms |
41 | a.Config = n.Config | 41 | a.Config = n.Config |
42 | a.ResolvedProvider = n.ResolvedProvider | ||
42 | 43 | ||
43 | return &NodeValidatableResourceInstance{ | 44 | return &NodeValidatableResourceInstance{ |
44 | NodeAbstractResource: a, | 45 | NodeAbstractResource: a, |
@@ -108,7 +109,7 @@ func (n *NodeValidatableResourceInstance) EvalTree() EvalNode { | |||
108 | Config: &n.Config.RawConfig, | 109 | Config: &n.Config.RawConfig, |
109 | }, | 110 | }, |
110 | &EvalGetProvider{ | 111 | &EvalGetProvider{ |
111 | Name: n.ProvidedBy()[0], | 112 | Name: n.ResolvedProvider, |
112 | Output: &provider, | 113 | Output: &provider, |
113 | }, | 114 | }, |
114 | &EvalInterpolate{ | 115 | &EvalInterpolate{ |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/path.go b/vendor/github.com/hashicorp/terraform/terraform/path.go index ca99685..51dd412 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/path.go +++ b/vendor/github.com/hashicorp/terraform/terraform/path.go | |||
@@ -1,24 +1,10 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "crypto/md5" | 4 | "strings" |
5 | "encoding/hex" | ||
6 | ) | 5 | ) |
7 | 6 | ||
8 | // PathCacheKey returns a cache key for a module path. | 7 | // PathCacheKey returns a cache key for a module path. |
9 | // | ||
10 | // TODO: test | ||
11 | func PathCacheKey(path []string) string { | 8 | func PathCacheKey(path []string) string { |
12 | // There is probably a better way to do this, but this is working for now. | 9 | return strings.Join(path, "|") |
13 | // We just create an MD5 hash of all the MD5 hashes of all the path | ||
14 | // elements. This gets us the property that it is unique per ordering. | ||
15 | hash := md5.New() | ||
16 | for _, p := range path { | ||
17 | single := md5.Sum([]byte(p)) | ||
18 | if _, err := hash.Write(single[:]); err != nil { | ||
19 | panic(err) | ||
20 | } | ||
21 | } | ||
22 | |||
23 | return hex.EncodeToString(hash.Sum(nil)) | ||
24 | } | 10 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/plan.go b/vendor/github.com/hashicorp/terraform/terraform/plan.go index 51d6652..30db195 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/plan.go +++ b/vendor/github.com/hashicorp/terraform/terraform/plan.go | |||
@@ -10,6 +10,7 @@ import ( | |||
10 | "sync" | 10 | "sync" |
11 | 11 | ||
12 | "github.com/hashicorp/terraform/config/module" | 12 | "github.com/hashicorp/terraform/config/module" |
13 | "github.com/hashicorp/terraform/version" | ||
13 | ) | 14 | ) |
14 | 15 | ||
15 | func init() { | 16 | func init() { |
@@ -26,18 +27,54 @@ func init() { | |||
26 | // necessary to make a change: the state, diff, config, backend config, etc. | 27 | // necessary to make a change: the state, diff, config, backend config, etc. |
27 | // This is so that it can run alone without any other data. | 28 | // This is so that it can run alone without any other data. |
28 | type Plan struct { | 29 | type Plan struct { |
29 | Diff *Diff | 30 | // Diff describes the resource actions that must be taken when this |
30 | Module *module.Tree | 31 | // plan is applied. |
31 | State *State | 32 | Diff *Diff |
32 | Vars map[string]interface{} | 33 | |
34 | // Module represents the entire configuration that was present when this | ||
35 | // plan was created. | ||
36 | Module *module.Tree | ||
37 | |||
38 | // State is the Terraform state that was current when this plan was | ||
39 | // created. | ||
40 | // | ||
41 | // It is not allowed to apply a plan that has a stale state, since its | ||
42 | // diff could be outdated. | ||
43 | State *State | ||
44 | |||
45 | // Vars retains the variables that were set when creating the plan, so | ||
46 | // that the same variables can be applied during apply. | ||
47 | Vars map[string]interface{} | ||
48 | |||
49 | // Targets, if non-empty, contains a set of resource address strings that | ||
50 | // identify graph nodes that were selected as targets for plan. | ||
51 | // | ||
52 | // When targets are set, any graph node that is not directly targeted or | ||
53 | // indirectly targeted via dependencies is excluded from the graph. | ||
33 | Targets []string | 54 | Targets []string |
34 | 55 | ||
56 | // TerraformVersion is the version of Terraform that was used to create | ||
57 | // this plan. | ||
58 | // | ||
59 | // It is not allowed to apply a plan created with a different version of | ||
60 | // Terraform, since the other fields of this structure may be interpreted | ||
61 | // in different ways between versions. | ||
35 | TerraformVersion string | 62 | TerraformVersion string |
36 | ProviderSHA256s map[string][]byte | 63 | |
64 | // ProviderSHA256s is a map giving the SHA256 hashes of the exact binaries | ||
65 | // used as plugins for each provider during plan. | ||
66 | // | ||
67 | // These must match between plan and apply to ensure that the diff is | ||
68 | // correctly interpreted, since different provider versions may have | ||
69 | // different attributes or attribute value constraints. | ||
70 | ProviderSHA256s map[string][]byte | ||
37 | 71 | ||
38 | // Backend is the backend that this plan should use and store data with. | 72 | // Backend is the backend that this plan should use and store data with. |
39 | Backend *BackendState | 73 | Backend *BackendState |
40 | 74 | ||
75 | // Destroy indicates that this plan was created for a full destroy operation | ||
76 | Destroy bool | ||
77 | |||
41 | once sync.Once | 78 | once sync.Once |
42 | } | 79 | } |
43 | 80 | ||
@@ -67,6 +104,7 @@ func (p *Plan) contextOpts(base *ContextOpts) (*ContextOpts, error) { | |||
67 | opts.Module = p.Module | 104 | opts.Module = p.Module |
68 | opts.Targets = p.Targets | 105 | opts.Targets = p.Targets |
69 | opts.ProviderSHA256s = p.ProviderSHA256s | 106 | opts.ProviderSHA256s = p.ProviderSHA256s |
107 | opts.Destroy = p.Destroy | ||
70 | 108 | ||
71 | if opts.State == nil { | 109 | if opts.State == nil { |
72 | opts.State = p.State | 110 | opts.State = p.State |
@@ -79,10 +117,10 @@ func (p *Plan) contextOpts(base *ContextOpts) (*ContextOpts, error) { | |||
79 | // the state, there is little chance that these aren't actually equal. | 117 | // the state, there is little chance that these aren't actually equal. |
80 | // Log the error condition for reference, but continue with the state | 118 | // Log the error condition for reference, but continue with the state |
81 | // we have. | 119 | // we have. |
82 | log.Println("[WARNING] Plan state and ContextOpts state are not equal") | 120 | log.Println("[WARN] Plan state and ContextOpts state are not equal") |
83 | } | 121 | } |
84 | 122 | ||
85 | thisVersion := VersionString() | 123 | thisVersion := version.String() |
86 | if p.TerraformVersion != "" && p.TerraformVersion != thisVersion { | 124 | if p.TerraformVersion != "" && p.TerraformVersion != thisVersion { |
87 | return nil, fmt.Errorf( | 125 | return nil, fmt.Errorf( |
88 | "plan was created with a different version of Terraform (created with %s, but running %s)", | 126 | "plan was created with a different version of Terraform (created with %s, but running %s)", |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource.go b/vendor/github.com/hashicorp/terraform/terraform/resource.go index 0acf0be..2f5ebb5 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/resource.go +++ b/vendor/github.com/hashicorp/terraform/terraform/resource.go | |||
@@ -88,6 +88,46 @@ func (i *InstanceInfo) HumanId() string { | |||
88 | i.Id) | 88 | i.Id) |
89 | } | 89 | } |
90 | 90 | ||
91 | // ResourceAddress returns the address of the resource that the receiver is describing. | ||
92 | func (i *InstanceInfo) ResourceAddress() *ResourceAddress { | ||
93 | // GROSS: for tainted and deposed instances, their status gets appended | ||
94 | // to i.Id to create a unique id for the graph node. Historically these | ||
95 | // ids were displayed to the user, so it's designed to be human-readable: | ||
96 | // "aws_instance.bar.0 (deposed #0)" | ||
97 | // | ||
98 | // So here we detect such suffixes and try to interpret them back to | ||
99 | // their original meaning so we can then produce a ResourceAddress | ||
100 | // with a suitable InstanceType. | ||
101 | id := i.Id | ||
102 | instanceType := TypeInvalid | ||
103 | if idx := strings.Index(id, " ("); idx != -1 { | ||
104 | remain := id[idx:] | ||
105 | id = id[:idx] | ||
106 | |||
107 | switch { | ||
108 | case strings.Contains(remain, "tainted"): | ||
109 | instanceType = TypeTainted | ||
110 | case strings.Contains(remain, "deposed"): | ||
111 | instanceType = TypeDeposed | ||
112 | } | ||
113 | } | ||
114 | |||
115 | addr, err := parseResourceAddressInternal(id) | ||
116 | if err != nil { | ||
117 | // should never happen, since that would indicate a bug in the | ||
118 | // code that constructed this InstanceInfo. | ||
119 | panic(fmt.Errorf("InstanceInfo has invalid Id %s", id)) | ||
120 | } | ||
121 | if len(i.ModulePath) > 1 { | ||
122 | addr.Path = i.ModulePath[1:] // trim off "root" prefix, which is implied | ||
123 | } | ||
124 | if instanceType != TypeInvalid { | ||
125 | addr.InstanceTypeSet = true | ||
126 | addr.InstanceType = instanceType | ||
127 | } | ||
128 | return addr | ||
129 | } | ||
130 | |||
91 | func (i *InstanceInfo) uniqueId() string { | 131 | func (i *InstanceInfo) uniqueId() string { |
92 | prefix := i.HumanId() | 132 | prefix := i.HumanId() |
93 | if v := i.uniqueExtra; v != "" { | 133 | if v := i.uniqueExtra; v != "" { |
@@ -306,7 +346,7 @@ func (c *ResourceConfig) get( | |||
306 | if err != nil { | 346 | if err != nil { |
307 | return nil, false | 347 | return nil, false |
308 | } | 348 | } |
309 | if i >= int64(cv.Len()) { | 349 | if int(i) < 0 || int(i) >= cv.Len() { |
310 | return nil, false | 350 | return nil, false |
311 | } | 351 | } |
312 | current = cv.Index(int(i)).Interface() | 352 | current = cv.Index(int(i)).Interface() |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_address.go b/vendor/github.com/hashicorp/terraform/terraform/resource_address.go index 8badca8..a64f5d8 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/resource_address.go +++ b/vendor/github.com/hashicorp/terraform/terraform/resource_address.go | |||
@@ -42,9 +42,9 @@ func (r *ResourceAddress) Copy() *ResourceAddress { | |||
42 | Type: r.Type, | 42 | Type: r.Type, |
43 | Mode: r.Mode, | 43 | Mode: r.Mode, |
44 | } | 44 | } |
45 | for _, p := range r.Path { | 45 | |
46 | n.Path = append(n.Path, p) | 46 | n.Path = append(n.Path, r.Path...) |
47 | } | 47 | |
48 | return n | 48 | return n |
49 | } | 49 | } |
50 | 50 | ||
@@ -362,40 +362,41 @@ func (addr *ResourceAddress) Less(other *ResourceAddress) bool { | |||
362 | 362 | ||
363 | switch { | 363 | switch { |
364 | 364 | ||
365 | case len(addr.Path) < len(other.Path): | 365 | case len(addr.Path) != len(other.Path): |
366 | return true | 366 | return len(addr.Path) < len(other.Path) |
367 | 367 | ||
368 | case !reflect.DeepEqual(addr.Path, other.Path): | 368 | case !reflect.DeepEqual(addr.Path, other.Path): |
369 | // If the two paths are the same length but don't match, we'll just | 369 | // If the two paths are the same length but don't match, we'll just |
370 | // cheat and compare the string forms since it's easier than | 370 | // cheat and compare the string forms since it's easier than |
371 | // comparing all of the path segments in turn. | 371 | // comparing all of the path segments in turn, and lexicographic |
372 | // comparison is correct for the module path portion. | ||
372 | addrStr := addr.String() | 373 | addrStr := addr.String() |
373 | otherStr := other.String() | 374 | otherStr := other.String() |
374 | return addrStr < otherStr | 375 | return addrStr < otherStr |
375 | 376 | ||
376 | case addr.Mode == config.DataResourceMode && other.Mode != config.DataResourceMode: | 377 | case addr.Mode != other.Mode: |
377 | return true | 378 | return addr.Mode == config.DataResourceMode |
378 | 379 | ||
379 | case addr.Type < other.Type: | 380 | case addr.Type != other.Type: |
380 | return true | 381 | return addr.Type < other.Type |
381 | 382 | ||
382 | case addr.Name < other.Name: | 383 | case addr.Name != other.Name: |
383 | return true | 384 | return addr.Name < other.Name |
384 | 385 | ||
385 | case addr.Index < other.Index: | 386 | case addr.Index != other.Index: |
386 | // Since "Index" is -1 for an un-indexed address, this also conveniently | 387 | // Since "Index" is -1 for an un-indexed address, this also conveniently |
387 | // sorts unindexed addresses before indexed ones, should they both | 388 | // sorts unindexed addresses before indexed ones, should they both |
388 | // appear for some reason. | 389 | // appear for some reason. |
389 | return true | 390 | return addr.Index < other.Index |
390 | 391 | ||
391 | case other.InstanceTypeSet && !addr.InstanceTypeSet: | 392 | case addr.InstanceTypeSet != other.InstanceTypeSet: |
392 | return true | 393 | return !addr.InstanceTypeSet |
393 | 394 | ||
394 | case addr.InstanceType < other.InstanceType: | 395 | case addr.InstanceType != other.InstanceType: |
395 | // InstanceType is actually an enum, so this is just an arbitrary | 396 | // InstanceType is actually an enum, so this is just an arbitrary |
396 | // sort based on the enum numeric values, and thus not particularly | 397 | // sort based on the enum numeric values, and thus not particularly |
397 | // meaningful. | 398 | // meaningful. |
398 | return true | 399 | return addr.InstanceType < other.InstanceType |
399 | 400 | ||
400 | default: | 401 | default: |
401 | return false | 402 | return false |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go b/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go index 7d78f67..93fd14f 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/resource_provider.go | |||
@@ -21,6 +21,15 @@ type ResourceProvider interface { | |||
21 | * Functions related to the provider | 21 | * Functions related to the provider |
22 | *********************************************************************/ | 22 | *********************************************************************/ |
23 | 23 | ||
24 | // ProviderSchema returns the config schema for the main provider | ||
25 | // configuration, as would appear in a "provider" block in the | ||
26 | // configuration files. | ||
27 | // | ||
28 | // Currently not all providers support schema. Callers must therefore | ||
29 | // first call Resources and DataSources and ensure that at least one | ||
30 | // resource or data source has the SchemaAvailable flag set. | ||
31 | GetSchema(*ProviderSchemaRequest) (*ProviderSchema, error) | ||
32 | |||
24 | // Input is called to ask the provider to ask the user for input | 33 | // Input is called to ask the provider to ask the user for input |
25 | // for completing the configuration if necesarry. | 34 | // for completing the configuration if necesarry. |
26 | // | 35 | // |
@@ -183,11 +192,25 @@ type ResourceProviderCloser interface { | |||
183 | type ResourceType struct { | 192 | type ResourceType struct { |
184 | Name string // Name of the resource, example "instance" (no provider prefix) | 193 | Name string // Name of the resource, example "instance" (no provider prefix) |
185 | Importable bool // Whether this resource supports importing | 194 | Importable bool // Whether this resource supports importing |
195 | |||
196 | // SchemaAvailable is set if the provider supports the ProviderSchema, | ||
197 | // ResourceTypeSchema and DataSourceSchema methods. Although it is | ||
198 | // included on each resource type, it's actually a provider-wide setting | ||
199 | // that's smuggled here only because that avoids a breaking change to | ||
200 | // the plugin protocol. | ||
201 | SchemaAvailable bool | ||
186 | } | 202 | } |
187 | 203 | ||
188 | // DataSource is a data source that a resource provider implements. | 204 | // DataSource is a data source that a resource provider implements. |
189 | type DataSource struct { | 205 | type DataSource struct { |
190 | Name string | 206 | Name string |
207 | |||
208 | // SchemaAvailable is set if the provider supports the ProviderSchema, | ||
209 | // ResourceTypeSchema and DataSourceSchema methods. Although it is | ||
210 | // included on each resource type, it's actually a provider-wide setting | ||
211 | // that's smuggled here only because that avoids a breaking change to | ||
212 | // the plugin protocol. | ||
213 | SchemaAvailable bool | ||
191 | } | 214 | } |
192 | 215 | ||
193 | // ResourceProviderResolver is an interface implemented by objects that are | 216 | // ResourceProviderResolver is an interface implemented by objects that are |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go b/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go index f531533..4000e3d 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go +++ b/vendor/github.com/hashicorp/terraform/terraform/resource_provider_mock.go | |||
@@ -1,6 +1,8 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import "sync" | 3 | import ( |
4 | "sync" | ||
5 | ) | ||
4 | 6 | ||
5 | // MockResourceProvider implements ResourceProvider but mocks out all the | 7 | // MockResourceProvider implements ResourceProvider but mocks out all the |
6 | // calls for testing purposes. | 8 | // calls for testing purposes. |
@@ -12,6 +14,10 @@ type MockResourceProvider struct { | |||
12 | 14 | ||
13 | CloseCalled bool | 15 | CloseCalled bool |
14 | CloseError error | 16 | CloseError error |
17 | GetSchemaCalled bool | ||
18 | GetSchemaRequest *ProviderSchemaRequest | ||
19 | GetSchemaReturn *ProviderSchema | ||
20 | GetSchemaReturnError error | ||
15 | InputCalled bool | 21 | InputCalled bool |
16 | InputInput UIInput | 22 | InputInput UIInput |
17 | InputConfig *ResourceConfig | 23 | InputConfig *ResourceConfig |
@@ -92,8 +98,19 @@ func (p *MockResourceProvider) Close() error { | |||
92 | return p.CloseError | 98 | return p.CloseError |
93 | } | 99 | } |
94 | 100 | ||
101 | func (p *MockResourceProvider) GetSchema(req *ProviderSchemaRequest) (*ProviderSchema, error) { | ||
102 | p.Lock() | ||
103 | defer p.Unlock() | ||
104 | |||
105 | p.GetSchemaCalled = true | ||
106 | p.GetSchemaRequest = req | ||
107 | return p.GetSchemaReturn, p.GetSchemaReturnError | ||
108 | } | ||
109 | |||
95 | func (p *MockResourceProvider) Input( | 110 | func (p *MockResourceProvider) Input( |
96 | input UIInput, c *ResourceConfig) (*ResourceConfig, error) { | 111 | input UIInput, c *ResourceConfig) (*ResourceConfig, error) { |
112 | p.Lock() | ||
113 | defer p.Unlock() | ||
97 | p.InputCalled = true | 114 | p.InputCalled = true |
98 | p.InputInput = input | 115 | p.InputInput = input |
99 | p.InputConfig = c | 116 | p.InputConfig = c |
@@ -186,6 +203,7 @@ func (p *MockResourceProvider) Diff( | |||
186 | p.DiffInfo = info | 203 | p.DiffInfo = info |
187 | p.DiffState = state | 204 | p.DiffState = state |
188 | p.DiffDesired = desired | 205 | p.DiffDesired = desired |
206 | |||
189 | if p.DiffFn != nil { | 207 | if p.DiffFn != nil { |
190 | return p.DiffFn(info, state, desired) | 208 | return p.DiffFn(info, state, desired) |
191 | } | 209 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/schemas.go b/vendor/github.com/hashicorp/terraform/terraform/schemas.go new file mode 100644 index 0000000..ec46efc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/schemas.go | |||
@@ -0,0 +1,34 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/terraform/config/configschema" | ||
5 | ) | ||
6 | |||
7 | type Schemas struct { | ||
8 | Providers ProviderSchemas | ||
9 | } | ||
10 | |||
11 | // ProviderSchemas is a map from provider names to provider schemas. | ||
12 | // | ||
13 | // The names in this map are the direct plugin name (e.g. "aws") rather than | ||
14 | // any alias name (e.g. "aws.foo"), since. | ||
15 | type ProviderSchemas map[string]*ProviderSchema | ||
16 | |||
17 | // ProviderSchema represents the schema for a provider's own configuration | ||
18 | // and the configuration for some or all of its resources and data sources. | ||
19 | // | ||
20 | // The completeness of this structure depends on how it was constructed. | ||
21 | // When constructed for a configuration, it will generally include only | ||
22 | // resource types and data sources used by that configuration. | ||
23 | type ProviderSchema struct { | ||
24 | Provider *configschema.Block | ||
25 | ResourceTypes map[string]*configschema.Block | ||
26 | DataSources map[string]*configschema.Block | ||
27 | } | ||
28 | |||
29 | // ProviderSchemaRequest is used to describe to a ResourceProvider which | ||
30 | // aspects of schema are required, when calling the GetSchema method. | ||
31 | type ProviderSchemaRequest struct { | ||
32 | ResourceTypes []string | ||
33 | DataSources []string | ||
34 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow.go b/vendor/github.com/hashicorp/terraform/terraform/shadow.go deleted file mode 100644 index 4632559..0000000 --- a/vendor/github.com/hashicorp/terraform/terraform/shadow.go +++ /dev/null | |||
@@ -1,28 +0,0 @@ | |||
1 | package terraform | ||
2 | |||
3 | // Shadow is the interface that any "shadow" structures must implement. | ||
4 | // | ||
5 | // A shadow structure is an interface implementation (typically) that | ||
6 | // shadows a real implementation and verifies that the same behavior occurs | ||
7 | // on both. The semantics of this behavior are up to the interface itself. | ||
8 | // | ||
9 | // A shadow NEVER modifies real values or state. It must always be safe to use. | ||
10 | // | ||
11 | // For example, a ResourceProvider shadow ensures that the same operations | ||
12 | // are done on the same resources with the same configurations. | ||
13 | // | ||
14 | // The typical usage of a shadow following this interface is to complete | ||
15 | // the real operations, then call CloseShadow which tells the shadow that | ||
16 | // the real side is done. Then, once the shadow is also complete, call | ||
17 | // ShadowError to find any errors that may have been caught. | ||
18 | type Shadow interface { | ||
19 | // CloseShadow tells the shadow that the REAL implementation is | ||
20 | // complete. Therefore, any calls that would block should now return | ||
21 | // immediately since no more changes will happen to the real side. | ||
22 | CloseShadow() error | ||
23 | |||
24 | // ShadowError returns the errors that the shadow has found. | ||
25 | // This should be called AFTER CloseShadow and AFTER the shadow is | ||
26 | // known to be complete (no more calls to it). | ||
27 | ShadowError() error | ||
28 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go deleted file mode 100644 index 116cf84..0000000 --- a/vendor/github.com/hashicorp/terraform/terraform/shadow_components.go +++ /dev/null | |||
@@ -1,273 +0,0 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "sync" | ||
6 | |||
7 | "github.com/hashicorp/go-multierror" | ||
8 | "github.com/hashicorp/terraform/helper/shadow" | ||
9 | ) | ||
10 | |||
11 | // newShadowComponentFactory creates a shadowed contextComponentFactory | ||
12 | // so that requests to create new components result in both a real and | ||
13 | // shadow side. | ||
14 | func newShadowComponentFactory( | ||
15 | f contextComponentFactory) (contextComponentFactory, *shadowComponentFactory) { | ||
16 | // Create the shared data | ||
17 | shared := &shadowComponentFactoryShared{contextComponentFactory: f} | ||
18 | |||
19 | // Create the real side | ||
20 | real := &shadowComponentFactory{ | ||
21 | shadowComponentFactoryShared: shared, | ||
22 | } | ||
23 | |||
24 | // Create the shadow | ||
25 | shadow := &shadowComponentFactory{ | ||
26 | shadowComponentFactoryShared: shared, | ||
27 | Shadow: true, | ||
28 | } | ||
29 | |||
30 | return real, shadow | ||
31 | } | ||
32 | |||
33 | // shadowComponentFactory is the shadow side. Any components created | ||
34 | // with this factory are fake and will not cause real work to happen. | ||
35 | // | ||
36 | // Unlike other shadowers, the shadow component factory will allow the | ||
37 | // shadow to create _any_ component even if it is never requested on the | ||
38 | // real side. This is because errors will happen later downstream as function | ||
39 | // calls are made to the shadows that are never matched on the real side. | ||
40 | type shadowComponentFactory struct { | ||
41 | *shadowComponentFactoryShared | ||
42 | |||
43 | Shadow bool // True if this should return the shadow | ||
44 | lock sync.Mutex | ||
45 | } | ||
46 | |||
47 | func (f *shadowComponentFactory) ResourceProvider( | ||
48 | n, uid string) (ResourceProvider, error) { | ||
49 | f.lock.Lock() | ||
50 | defer f.lock.Unlock() | ||
51 | |||
52 | real, shadow, err := f.shadowComponentFactoryShared.ResourceProvider(n, uid) | ||
53 | var result ResourceProvider = real | ||
54 | if f.Shadow { | ||
55 | result = shadow | ||
56 | } | ||
57 | |||
58 | return result, err | ||
59 | } | ||
60 | |||
61 | func (f *shadowComponentFactory) ResourceProvisioner( | ||
62 | n, uid string) (ResourceProvisioner, error) { | ||
63 | f.lock.Lock() | ||
64 | defer f.lock.Unlock() | ||
65 | |||
66 | real, shadow, err := f.shadowComponentFactoryShared.ResourceProvisioner(n, uid) | ||
67 | var result ResourceProvisioner = real | ||
68 | if f.Shadow { | ||
69 | result = shadow | ||
70 | } | ||
71 | |||
72 | return result, err | ||
73 | } | ||
74 | |||
75 | // CloseShadow is called when the _real_ side is complete. This will cause | ||
76 | // all future blocking operations to return immediately on the shadow to | ||
77 | // ensure the shadow also completes. | ||
78 | func (f *shadowComponentFactory) CloseShadow() error { | ||
79 | // If we aren't the shadow, just return | ||
80 | if !f.Shadow { | ||
81 | return nil | ||
82 | } | ||
83 | |||
84 | // Lock ourselves so we don't modify state | ||
85 | f.lock.Lock() | ||
86 | defer f.lock.Unlock() | ||
87 | |||
88 | // Grab our shared state | ||
89 | shared := f.shadowComponentFactoryShared | ||
90 | |||
91 | // If we're already closed, its an error | ||
92 | if shared.closed { | ||
93 | return fmt.Errorf("component factory shadow already closed") | ||
94 | } | ||
95 | |||
96 | // Close all the providers and provisioners and return the error | ||
97 | var result error | ||
98 | for _, n := range shared.providerKeys { | ||
99 | _, shadow, err := shared.ResourceProvider(n, n) | ||
100 | if err == nil && shadow != nil { | ||
101 | if err := shadow.CloseShadow(); err != nil { | ||
102 | result = multierror.Append(result, err) | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | |||
107 | for _, n := range shared.provisionerKeys { | ||
108 | _, shadow, err := shared.ResourceProvisioner(n, n) | ||
109 | if err == nil && shadow != nil { | ||
110 | if err := shadow.CloseShadow(); err != nil { | ||
111 | result = multierror.Append(result, err) | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | |||
116 | // Mark ourselves as closed | ||
117 | shared.closed = true | ||
118 | |||
119 | return result | ||
120 | } | ||
121 | |||
122 | func (f *shadowComponentFactory) ShadowError() error { | ||
123 | // If we aren't the shadow, just return | ||
124 | if !f.Shadow { | ||
125 | return nil | ||
126 | } | ||
127 | |||
128 | // Lock ourselves so we don't modify state | ||
129 | f.lock.Lock() | ||
130 | defer f.lock.Unlock() | ||
131 | |||
132 | // Grab our shared state | ||
133 | shared := f.shadowComponentFactoryShared | ||
134 | |||
135 | // If we're not closed, its an error | ||
136 | if !shared.closed { | ||
137 | return fmt.Errorf("component factory must be closed to retrieve errors") | ||
138 | } | ||
139 | |||
140 | // Close all the providers and provisioners and return the error | ||
141 | var result error | ||
142 | for _, n := range shared.providerKeys { | ||
143 | _, shadow, err := shared.ResourceProvider(n, n) | ||
144 | if err == nil && shadow != nil { | ||
145 | if err := shadow.ShadowError(); err != nil { | ||
146 | result = multierror.Append(result, err) | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | |||
151 | for _, n := range shared.provisionerKeys { | ||
152 | _, shadow, err := shared.ResourceProvisioner(n, n) | ||
153 | if err == nil && shadow != nil { | ||
154 | if err := shadow.ShadowError(); err != nil { | ||
155 | result = multierror.Append(result, err) | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | return result | ||
161 | } | ||
162 | |||
163 | // shadowComponentFactoryShared is shared data between the two factories. | ||
164 | // | ||
165 | // It is NOT SAFE to run any function on this struct in parallel. Lock | ||
166 | // access to this struct. | ||
167 | type shadowComponentFactoryShared struct { | ||
168 | contextComponentFactory | ||
169 | |||
170 | closed bool | ||
171 | providers shadow.KeyedValue | ||
172 | providerKeys []string | ||
173 | provisioners shadow.KeyedValue | ||
174 | provisionerKeys []string | ||
175 | } | ||
176 | |||
177 | // shadowResourceProviderFactoryEntry is the entry that is stored in | ||
178 | // the Shadows key/value for a provider. | ||
179 | type shadowComponentFactoryProviderEntry struct { | ||
180 | Real ResourceProvider | ||
181 | Shadow shadowResourceProvider | ||
182 | Err error | ||
183 | } | ||
184 | |||
185 | type shadowComponentFactoryProvisionerEntry struct { | ||
186 | Real ResourceProvisioner | ||
187 | Shadow shadowResourceProvisioner | ||
188 | Err error | ||
189 | } | ||
190 | |||
191 | func (f *shadowComponentFactoryShared) ResourceProvider( | ||
192 | n, uid string) (ResourceProvider, shadowResourceProvider, error) { | ||
193 | // Determine if we already have a value | ||
194 | raw, ok := f.providers.ValueOk(uid) | ||
195 | if !ok { | ||
196 | // Build the entry | ||
197 | var entry shadowComponentFactoryProviderEntry | ||
198 | |||
199 | // No value, initialize. Create the original | ||
200 | p, err := f.contextComponentFactory.ResourceProvider(n, uid) | ||
201 | if err != nil { | ||
202 | entry.Err = err | ||
203 | p = nil // Just to be sure | ||
204 | } | ||
205 | |||
206 | if p != nil { | ||
207 | // Create the shadow | ||
208 | real, shadow := newShadowResourceProvider(p) | ||
209 | entry.Real = real | ||
210 | entry.Shadow = shadow | ||
211 | |||
212 | if f.closed { | ||
213 | shadow.CloseShadow() | ||
214 | } | ||
215 | } | ||
216 | |||
217 | // Store the value | ||
218 | f.providers.SetValue(uid, &entry) | ||
219 | f.providerKeys = append(f.providerKeys, uid) | ||
220 | raw = &entry | ||
221 | } | ||
222 | |||
223 | // Read the entry | ||
224 | entry, ok := raw.(*shadowComponentFactoryProviderEntry) | ||
225 | if !ok { | ||
226 | return nil, nil, fmt.Errorf("Unknown value for shadow provider: %#v", raw) | ||
227 | } | ||
228 | |||
229 | // Return | ||
230 | return entry.Real, entry.Shadow, entry.Err | ||
231 | } | ||
232 | |||
233 | func (f *shadowComponentFactoryShared) ResourceProvisioner( | ||
234 | n, uid string) (ResourceProvisioner, shadowResourceProvisioner, error) { | ||
235 | // Determine if we already have a value | ||
236 | raw, ok := f.provisioners.ValueOk(uid) | ||
237 | if !ok { | ||
238 | // Build the entry | ||
239 | var entry shadowComponentFactoryProvisionerEntry | ||
240 | |||
241 | // No value, initialize. Create the original | ||
242 | p, err := f.contextComponentFactory.ResourceProvisioner(n, uid) | ||
243 | if err != nil { | ||
244 | entry.Err = err | ||
245 | p = nil // Just to be sure | ||
246 | } | ||
247 | |||
248 | if p != nil { | ||
249 | // For now, just create a mock since we don't support provisioners yet | ||
250 | real, shadow := newShadowResourceProvisioner(p) | ||
251 | entry.Real = real | ||
252 | entry.Shadow = shadow | ||
253 | |||
254 | if f.closed { | ||
255 | shadow.CloseShadow() | ||
256 | } | ||
257 | } | ||
258 | |||
259 | // Store the value | ||
260 | f.provisioners.SetValue(uid, &entry) | ||
261 | f.provisionerKeys = append(f.provisionerKeys, uid) | ||
262 | raw = &entry | ||
263 | } | ||
264 | |||
265 | // Read the entry | ||
266 | entry, ok := raw.(*shadowComponentFactoryProvisionerEntry) | ||
267 | if !ok { | ||
268 | return nil, nil, fmt.Errorf("Unknown value for shadow provisioner: %#v", raw) | ||
269 | } | ||
270 | |||
271 | // Return | ||
272 | return entry.Real, entry.Shadow, entry.Err | ||
273 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_context.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_context.go deleted file mode 100644 index 5588af2..0000000 --- a/vendor/github.com/hashicorp/terraform/terraform/shadow_context.go +++ /dev/null | |||
@@ -1,158 +0,0 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "strings" | ||
6 | |||
7 | "github.com/hashicorp/go-multierror" | ||
8 | "github.com/mitchellh/copystructure" | ||
9 | ) | ||
10 | |||
11 | // newShadowContext creates a new context that will shadow the given context | ||
12 | // when walking the graph. The resulting context should be used _only once_ | ||
13 | // for a graph walk. | ||
14 | // | ||
15 | // The returned Shadow should be closed after the graph walk with the | ||
16 | // real context is complete. Errors from the shadow can be retrieved there. | ||
17 | // | ||
18 | // Most importantly, any operations done on the shadow context (the returned | ||
19 | // context) will NEVER affect the real context. All structures are deep | ||
20 | // copied, no real providers or resources are used, etc. | ||
21 | func newShadowContext(c *Context) (*Context, *Context, Shadow) { | ||
22 | // Copy the targets | ||
23 | targetRaw, err := copystructure.Copy(c.targets) | ||
24 | if err != nil { | ||
25 | panic(err) | ||
26 | } | ||
27 | |||
28 | // Copy the variables | ||
29 | varRaw, err := copystructure.Copy(c.variables) | ||
30 | if err != nil { | ||
31 | panic(err) | ||
32 | } | ||
33 | |||
34 | // Copy the provider inputs | ||
35 | providerInputRaw, err := copystructure.Copy(c.providerInputConfig) | ||
36 | if err != nil { | ||
37 | panic(err) | ||
38 | } | ||
39 | |||
40 | // The factories | ||
41 | componentsReal, componentsShadow := newShadowComponentFactory(c.components) | ||
42 | |||
43 | // Create the shadow | ||
44 | shadow := &Context{ | ||
45 | components: componentsShadow, | ||
46 | destroy: c.destroy, | ||
47 | diff: c.diff.DeepCopy(), | ||
48 | hooks: nil, | ||
49 | meta: c.meta, | ||
50 | module: c.module, | ||
51 | state: c.state.DeepCopy(), | ||
52 | targets: targetRaw.([]string), | ||
53 | variables: varRaw.(map[string]interface{}), | ||
54 | |||
55 | // NOTE(mitchellh): This is not going to work for shadows that are | ||
56 | // testing that input results in the proper end state. At the time | ||
57 | // of writing, input is not used in any state-changing graph | ||
58 | // walks anyways, so this checks nothing. We set it to this to avoid | ||
59 | // any panics but even a "nil" value worked here. | ||
60 | uiInput: new(MockUIInput), | ||
61 | |||
62 | // Hardcoded to 4 since parallelism in the shadow doesn't matter | ||
63 | // a ton since we're doing far less compared to the real side | ||
64 | // and our operations are MUCH faster. | ||
65 | parallelSem: NewSemaphore(4), | ||
66 | providerInputConfig: providerInputRaw.(map[string]map[string]interface{}), | ||
67 | } | ||
68 | |||
69 | // Create the real context. This is effectively just a copy of | ||
70 | // the context given except we need to modify some of the values | ||
71 | // to point to the real side of a shadow so the shadow can compare values. | ||
72 | real := &Context{ | ||
73 | // The fields below are changed. | ||
74 | components: componentsReal, | ||
75 | |||
76 | // The fields below are direct copies | ||
77 | destroy: c.destroy, | ||
78 | diff: c.diff, | ||
79 | // diffLock - no copy | ||
80 | hooks: c.hooks, | ||
81 | meta: c.meta, | ||
82 | module: c.module, | ||
83 | sh: c.sh, | ||
84 | state: c.state, | ||
85 | // stateLock - no copy | ||
86 | targets: c.targets, | ||
87 | uiInput: c.uiInput, | ||
88 | variables: c.variables, | ||
89 | |||
90 | // l - no copy | ||
91 | parallelSem: c.parallelSem, | ||
92 | providerInputConfig: c.providerInputConfig, | ||
93 | runContext: c.runContext, | ||
94 | runContextCancel: c.runContextCancel, | ||
95 | shadowErr: c.shadowErr, | ||
96 | } | ||
97 | |||
98 | return real, shadow, &shadowContextCloser{ | ||
99 | Components: componentsShadow, | ||
100 | } | ||
101 | } | ||
102 | |||
103 | // shadowContextVerify takes the real and shadow context and verifies they | ||
104 | // have equal diffs and states. | ||
105 | func shadowContextVerify(real, shadow *Context) error { | ||
106 | var result error | ||
107 | |||
108 | // The states compared must be pruned so they're minimal/clean | ||
109 | real.state.prune() | ||
110 | shadow.state.prune() | ||
111 | |||
112 | // Compare the states | ||
113 | if !real.state.Equal(shadow.state) { | ||
114 | result = multierror.Append(result, fmt.Errorf( | ||
115 | "Real and shadow states do not match! "+ | ||
116 | "Real state:\n\n%s\n\n"+ | ||
117 | "Shadow state:\n\n%s\n\n", | ||
118 | real.state, shadow.state)) | ||
119 | } | ||
120 | |||
121 | // Compare the diffs | ||
122 | if !real.diff.Equal(shadow.diff) { | ||
123 | result = multierror.Append(result, fmt.Errorf( | ||
124 | "Real and shadow diffs do not match! "+ | ||
125 | "Real diff:\n\n%s\n\n"+ | ||
126 | "Shadow diff:\n\n%s\n\n", | ||
127 | real.diff, shadow.diff)) | ||
128 | } | ||
129 | |||
130 | return result | ||
131 | } | ||
132 | |||
133 | // shadowContextCloser is the io.Closer returned by newShadowContext that | ||
134 | // closes all the shadows and returns the results. | ||
135 | type shadowContextCloser struct { | ||
136 | Components *shadowComponentFactory | ||
137 | } | ||
138 | |||
139 | // Close closes the shadow context. | ||
140 | func (c *shadowContextCloser) CloseShadow() error { | ||
141 | return c.Components.CloseShadow() | ||
142 | } | ||
143 | |||
144 | func (c *shadowContextCloser) ShadowError() error { | ||
145 | err := c.Components.ShadowError() | ||
146 | if err == nil { | ||
147 | return nil | ||
148 | } | ||
149 | |||
150 | // This is a sad edge case: if the configuration contains uuid() at | ||
151 | // any point, we cannot reason aboyt the shadow execution. Tested | ||
152 | // with Context2Plan_shadowUuid. | ||
153 | if strings.Contains(err.Error(), "uuid()") { | ||
154 | err = nil | ||
155 | } | ||
156 | |||
157 | return err | ||
158 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provider.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provider.go deleted file mode 100644 index 9741d7e..0000000 --- a/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provider.go +++ /dev/null | |||
@@ -1,815 +0,0 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "log" | ||
6 | "sync" | ||
7 | |||
8 | "github.com/hashicorp/go-multierror" | ||
9 | "github.com/hashicorp/terraform/helper/shadow" | ||
10 | ) | ||
11 | |||
12 | // shadowResourceProvider implements ResourceProvider for the shadow | ||
13 | // eval context defined in eval_context_shadow.go. | ||
14 | // | ||
15 | // This is used to verify behavior with a real provider. This shouldn't | ||
16 | // be used directly. | ||
17 | type shadowResourceProvider interface { | ||
18 | ResourceProvider | ||
19 | Shadow | ||
20 | } | ||
21 | |||
22 | // newShadowResourceProvider creates a new shadowed ResourceProvider. | ||
23 | // | ||
24 | // This will assume a well behaved real ResourceProvider. For example, | ||
25 | // it assumes that the `Resources` call underneath doesn't change values | ||
26 | // since once it is called on the real provider, it will be cached and | ||
27 | // returned in the shadow since number of calls to that shouldn't affect | ||
28 | // actual behavior. | ||
29 | // | ||
30 | // However, with calls like Apply, call order is taken into account, | ||
31 | // parameters are checked for equality, etc. | ||
32 | func newShadowResourceProvider(p ResourceProvider) (ResourceProvider, shadowResourceProvider) { | ||
33 | // Create the shared data | ||
34 | shared := shadowResourceProviderShared{} | ||
35 | |||
36 | // Create the real provider that does actual work | ||
37 | real := &shadowResourceProviderReal{ | ||
38 | ResourceProvider: p, | ||
39 | Shared: &shared, | ||
40 | } | ||
41 | |||
42 | // Create the shadow that watches the real value | ||
43 | shadow := &shadowResourceProviderShadow{ | ||
44 | Shared: &shared, | ||
45 | |||
46 | resources: p.Resources(), | ||
47 | dataSources: p.DataSources(), | ||
48 | } | ||
49 | |||
50 | return real, shadow | ||
51 | } | ||
52 | |||
53 | // shadowResourceProviderReal is the real resource provider. Function calls | ||
54 | // to this will perform real work. This records the parameters and return | ||
55 | // values and call order for the shadow to reproduce. | ||
56 | type shadowResourceProviderReal struct { | ||
57 | ResourceProvider | ||
58 | |||
59 | Shared *shadowResourceProviderShared | ||
60 | } | ||
61 | |||
62 | func (p *shadowResourceProviderReal) Close() error { | ||
63 | var result error | ||
64 | if c, ok := p.ResourceProvider.(ResourceProviderCloser); ok { | ||
65 | result = c.Close() | ||
66 | } | ||
67 | |||
68 | p.Shared.CloseErr.SetValue(result) | ||
69 | return result | ||
70 | } | ||
71 | |||
72 | func (p *shadowResourceProviderReal) Input( | ||
73 | input UIInput, c *ResourceConfig) (*ResourceConfig, error) { | ||
74 | cCopy := c.DeepCopy() | ||
75 | |||
76 | result, err := p.ResourceProvider.Input(input, c) | ||
77 | p.Shared.Input.SetValue(&shadowResourceProviderInput{ | ||
78 | Config: cCopy, | ||
79 | Result: result.DeepCopy(), | ||
80 | ResultErr: err, | ||
81 | }) | ||
82 | |||
83 | return result, err | ||
84 | } | ||
85 | |||
86 | func (p *shadowResourceProviderReal) Validate(c *ResourceConfig) ([]string, []error) { | ||
87 | warns, errs := p.ResourceProvider.Validate(c) | ||
88 | p.Shared.Validate.SetValue(&shadowResourceProviderValidate{ | ||
89 | Config: c.DeepCopy(), | ||
90 | ResultWarn: warns, | ||
91 | ResultErr: errs, | ||
92 | }) | ||
93 | |||
94 | return warns, errs | ||
95 | } | ||
96 | |||
97 | func (p *shadowResourceProviderReal) Configure(c *ResourceConfig) error { | ||
98 | cCopy := c.DeepCopy() | ||
99 | |||
100 | err := p.ResourceProvider.Configure(c) | ||
101 | p.Shared.Configure.SetValue(&shadowResourceProviderConfigure{ | ||
102 | Config: cCopy, | ||
103 | Result: err, | ||
104 | }) | ||
105 | |||
106 | return err | ||
107 | } | ||
108 | |||
109 | func (p *shadowResourceProviderReal) Stop() error { | ||
110 | return p.ResourceProvider.Stop() | ||
111 | } | ||
112 | |||
113 | func (p *shadowResourceProviderReal) ValidateResource( | ||
114 | t string, c *ResourceConfig) ([]string, []error) { | ||
115 | key := t | ||
116 | configCopy := c.DeepCopy() | ||
117 | |||
118 | // Real operation | ||
119 | warns, errs := p.ResourceProvider.ValidateResource(t, c) | ||
120 | |||
121 | // Initialize to ensure we always have a wrapper with a lock | ||
122 | p.Shared.ValidateResource.Init( | ||
123 | key, &shadowResourceProviderValidateResourceWrapper{}) | ||
124 | |||
125 | // Get the result | ||
126 | raw := p.Shared.ValidateResource.Value(key) | ||
127 | wrapper, ok := raw.(*shadowResourceProviderValidateResourceWrapper) | ||
128 | if !ok { | ||
129 | // If this fails then we just continue with our day... the shadow | ||
130 | // will fail to but there isn't much we can do. | ||
131 | log.Printf( | ||
132 | "[ERROR] unknown value in ValidateResource shadow value: %#v", raw) | ||
133 | return warns, errs | ||
134 | } | ||
135 | |||
136 | // Lock the wrapper for writing and record our call | ||
137 | wrapper.Lock() | ||
138 | defer wrapper.Unlock() | ||
139 | |||
140 | wrapper.Calls = append(wrapper.Calls, &shadowResourceProviderValidateResource{ | ||
141 | Config: configCopy, | ||
142 | Warns: warns, | ||
143 | Errors: errs, | ||
144 | }) | ||
145 | |||
146 | // With it locked, call SetValue again so that it triggers WaitForChange | ||
147 | p.Shared.ValidateResource.SetValue(key, wrapper) | ||
148 | |||
149 | // Return the result | ||
150 | return warns, errs | ||
151 | } | ||
152 | |||
153 | func (p *shadowResourceProviderReal) Apply( | ||
154 | info *InstanceInfo, | ||
155 | state *InstanceState, | ||
156 | diff *InstanceDiff) (*InstanceState, error) { | ||
157 | // Thse have to be copied before the call since call can modify | ||
158 | stateCopy := state.DeepCopy() | ||
159 | diffCopy := diff.DeepCopy() | ||
160 | |||
161 | result, err := p.ResourceProvider.Apply(info, state, diff) | ||
162 | p.Shared.Apply.SetValue(info.uniqueId(), &shadowResourceProviderApply{ | ||
163 | State: stateCopy, | ||
164 | Diff: diffCopy, | ||
165 | Result: result.DeepCopy(), | ||
166 | ResultErr: err, | ||
167 | }) | ||
168 | |||
169 | return result, err | ||
170 | } | ||
171 | |||
172 | func (p *shadowResourceProviderReal) Diff( | ||
173 | info *InstanceInfo, | ||
174 | state *InstanceState, | ||
175 | desired *ResourceConfig) (*InstanceDiff, error) { | ||
176 | // Thse have to be copied before the call since call can modify | ||
177 | stateCopy := state.DeepCopy() | ||
178 | desiredCopy := desired.DeepCopy() | ||
179 | |||
180 | result, err := p.ResourceProvider.Diff(info, state, desired) | ||
181 | p.Shared.Diff.SetValue(info.uniqueId(), &shadowResourceProviderDiff{ | ||
182 | State: stateCopy, | ||
183 | Desired: desiredCopy, | ||
184 | Result: result.DeepCopy(), | ||
185 | ResultErr: err, | ||
186 | }) | ||
187 | |||
188 | return result, err | ||
189 | } | ||
190 | |||
191 | func (p *shadowResourceProviderReal) Refresh( | ||
192 | info *InstanceInfo, | ||
193 | state *InstanceState) (*InstanceState, error) { | ||
194 | // Thse have to be copied before the call since call can modify | ||
195 | stateCopy := state.DeepCopy() | ||
196 | |||
197 | result, err := p.ResourceProvider.Refresh(info, state) | ||
198 | p.Shared.Refresh.SetValue(info.uniqueId(), &shadowResourceProviderRefresh{ | ||
199 | State: stateCopy, | ||
200 | Result: result.DeepCopy(), | ||
201 | ResultErr: err, | ||
202 | }) | ||
203 | |||
204 | return result, err | ||
205 | } | ||
206 | |||
207 | func (p *shadowResourceProviderReal) ValidateDataSource( | ||
208 | t string, c *ResourceConfig) ([]string, []error) { | ||
209 | key := t | ||
210 | configCopy := c.DeepCopy() | ||
211 | |||
212 | // Real operation | ||
213 | warns, errs := p.ResourceProvider.ValidateDataSource(t, c) | ||
214 | |||
215 | // Initialize | ||
216 | p.Shared.ValidateDataSource.Init( | ||
217 | key, &shadowResourceProviderValidateDataSourceWrapper{}) | ||
218 | |||
219 | // Get the result | ||
220 | raw := p.Shared.ValidateDataSource.Value(key) | ||
221 | wrapper, ok := raw.(*shadowResourceProviderValidateDataSourceWrapper) | ||
222 | if !ok { | ||
223 | // If this fails then we just continue with our day... the shadow | ||
224 | // will fail to but there isn't much we can do. | ||
225 | log.Printf( | ||
226 | "[ERROR] unknown value in ValidateDataSource shadow value: %#v", raw) | ||
227 | return warns, errs | ||
228 | } | ||
229 | |||
230 | // Lock the wrapper for writing and record our call | ||
231 | wrapper.Lock() | ||
232 | defer wrapper.Unlock() | ||
233 | |||
234 | wrapper.Calls = append(wrapper.Calls, &shadowResourceProviderValidateDataSource{ | ||
235 | Config: configCopy, | ||
236 | Warns: warns, | ||
237 | Errors: errs, | ||
238 | }) | ||
239 | |||
240 | // Set it | ||
241 | p.Shared.ValidateDataSource.SetValue(key, wrapper) | ||
242 | |||
243 | // Return the result | ||
244 | return warns, errs | ||
245 | } | ||
246 | |||
247 | func (p *shadowResourceProviderReal) ReadDataDiff( | ||
248 | info *InstanceInfo, | ||
249 | desired *ResourceConfig) (*InstanceDiff, error) { | ||
250 | // These have to be copied before the call since call can modify | ||
251 | desiredCopy := desired.DeepCopy() | ||
252 | |||
253 | result, err := p.ResourceProvider.ReadDataDiff(info, desired) | ||
254 | p.Shared.ReadDataDiff.SetValue(info.uniqueId(), &shadowResourceProviderReadDataDiff{ | ||
255 | Desired: desiredCopy, | ||
256 | Result: result.DeepCopy(), | ||
257 | ResultErr: err, | ||
258 | }) | ||
259 | |||
260 | return result, err | ||
261 | } | ||
262 | |||
263 | func (p *shadowResourceProviderReal) ReadDataApply( | ||
264 | info *InstanceInfo, | ||
265 | diff *InstanceDiff) (*InstanceState, error) { | ||
266 | // Thse have to be copied before the call since call can modify | ||
267 | diffCopy := diff.DeepCopy() | ||
268 | |||
269 | result, err := p.ResourceProvider.ReadDataApply(info, diff) | ||
270 | p.Shared.ReadDataApply.SetValue(info.uniqueId(), &shadowResourceProviderReadDataApply{ | ||
271 | Diff: diffCopy, | ||
272 | Result: result.DeepCopy(), | ||
273 | ResultErr: err, | ||
274 | }) | ||
275 | |||
276 | return result, err | ||
277 | } | ||
278 | |||
279 | // shadowResourceProviderShadow is the shadow resource provider. Function | ||
280 | // calls never affect real resources. This is paired with the "real" side | ||
281 | // which must be called properly to enable recording. | ||
282 | type shadowResourceProviderShadow struct { | ||
283 | Shared *shadowResourceProviderShared | ||
284 | |||
285 | // Cached values that are expected to not change | ||
286 | resources []ResourceType | ||
287 | dataSources []DataSource | ||
288 | |||
289 | Error error // Error is the list of errors from the shadow | ||
290 | ErrorLock sync.Mutex | ||
291 | } | ||
292 | |||
293 | type shadowResourceProviderShared struct { | ||
294 | // NOTE: Anytime a value is added here, be sure to add it to | ||
295 | // the Close() method so that it is closed. | ||
296 | |||
297 | CloseErr shadow.Value | ||
298 | Input shadow.Value | ||
299 | Validate shadow.Value | ||
300 | Configure shadow.Value | ||
301 | ValidateResource shadow.KeyedValue | ||
302 | Apply shadow.KeyedValue | ||
303 | Diff shadow.KeyedValue | ||
304 | Refresh shadow.KeyedValue | ||
305 | ValidateDataSource shadow.KeyedValue | ||
306 | ReadDataDiff shadow.KeyedValue | ||
307 | ReadDataApply shadow.KeyedValue | ||
308 | } | ||
309 | |||
310 | func (p *shadowResourceProviderShared) Close() error { | ||
311 | return shadow.Close(p) | ||
312 | } | ||
313 | |||
314 | func (p *shadowResourceProviderShadow) CloseShadow() error { | ||
315 | err := p.Shared.Close() | ||
316 | if err != nil { | ||
317 | err = fmt.Errorf("close error: %s", err) | ||
318 | } | ||
319 | |||
320 | return err | ||
321 | } | ||
322 | |||
323 | func (p *shadowResourceProviderShadow) ShadowError() error { | ||
324 | return p.Error | ||
325 | } | ||
326 | |||
327 | func (p *shadowResourceProviderShadow) Resources() []ResourceType { | ||
328 | return p.resources | ||
329 | } | ||
330 | |||
331 | func (p *shadowResourceProviderShadow) DataSources() []DataSource { | ||
332 | return p.dataSources | ||
333 | } | ||
334 | |||
335 | func (p *shadowResourceProviderShadow) Close() error { | ||
336 | v := p.Shared.CloseErr.Value() | ||
337 | if v == nil { | ||
338 | return nil | ||
339 | } | ||
340 | |||
341 | return v.(error) | ||
342 | } | ||
343 | |||
344 | func (p *shadowResourceProviderShadow) Input( | ||
345 | input UIInput, c *ResourceConfig) (*ResourceConfig, error) { | ||
346 | // Get the result of the input call | ||
347 | raw := p.Shared.Input.Value() | ||
348 | if raw == nil { | ||
349 | return nil, nil | ||
350 | } | ||
351 | |||
352 | result, ok := raw.(*shadowResourceProviderInput) | ||
353 | if !ok { | ||
354 | p.ErrorLock.Lock() | ||
355 | defer p.ErrorLock.Unlock() | ||
356 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
357 | "Unknown 'input' shadow value: %#v", raw)) | ||
358 | return nil, nil | ||
359 | } | ||
360 | |||
361 | // Compare the parameters, which should be identical | ||
362 | if !c.Equal(result.Config) { | ||
363 | p.ErrorLock.Lock() | ||
364 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
365 | "Input had unequal configurations (real, then shadow):\n\n%#v\n\n%#v", | ||
366 | result.Config, c)) | ||
367 | p.ErrorLock.Unlock() | ||
368 | } | ||
369 | |||
370 | // Return the results | ||
371 | return result.Result, result.ResultErr | ||
372 | } | ||
373 | |||
374 | func (p *shadowResourceProviderShadow) Validate(c *ResourceConfig) ([]string, []error) { | ||
375 | // Get the result of the validate call | ||
376 | raw := p.Shared.Validate.Value() | ||
377 | if raw == nil { | ||
378 | return nil, nil | ||
379 | } | ||
380 | |||
381 | result, ok := raw.(*shadowResourceProviderValidate) | ||
382 | if !ok { | ||
383 | p.ErrorLock.Lock() | ||
384 | defer p.ErrorLock.Unlock() | ||
385 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
386 | "Unknown 'validate' shadow value: %#v", raw)) | ||
387 | return nil, nil | ||
388 | } | ||
389 | |||
390 | // Compare the parameters, which should be identical | ||
391 | if !c.Equal(result.Config) { | ||
392 | p.ErrorLock.Lock() | ||
393 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
394 | "Validate had unequal configurations (real, then shadow):\n\n%#v\n\n%#v", | ||
395 | result.Config, c)) | ||
396 | p.ErrorLock.Unlock() | ||
397 | } | ||
398 | |||
399 | // Return the results | ||
400 | return result.ResultWarn, result.ResultErr | ||
401 | } | ||
402 | |||
403 | func (p *shadowResourceProviderShadow) Configure(c *ResourceConfig) error { | ||
404 | // Get the result of the call | ||
405 | raw := p.Shared.Configure.Value() | ||
406 | if raw == nil { | ||
407 | return nil | ||
408 | } | ||
409 | |||
410 | result, ok := raw.(*shadowResourceProviderConfigure) | ||
411 | if !ok { | ||
412 | p.ErrorLock.Lock() | ||
413 | defer p.ErrorLock.Unlock() | ||
414 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
415 | "Unknown 'configure' shadow value: %#v", raw)) | ||
416 | return nil | ||
417 | } | ||
418 | |||
419 | // Compare the parameters, which should be identical | ||
420 | if !c.Equal(result.Config) { | ||
421 | p.ErrorLock.Lock() | ||
422 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
423 | "Configure had unequal configurations (real, then shadow):\n\n%#v\n\n%#v", | ||
424 | result.Config, c)) | ||
425 | p.ErrorLock.Unlock() | ||
426 | } | ||
427 | |||
428 | // Return the results | ||
429 | return result.Result | ||
430 | } | ||
431 | |||
432 | // Stop returns immediately. | ||
433 | func (p *shadowResourceProviderShadow) Stop() error { | ||
434 | return nil | ||
435 | } | ||
436 | |||
437 | func (p *shadowResourceProviderShadow) ValidateResource(t string, c *ResourceConfig) ([]string, []error) { | ||
438 | // Unique key | ||
439 | key := t | ||
440 | |||
441 | // Get the initial value | ||
442 | raw := p.Shared.ValidateResource.Value(key) | ||
443 | |||
444 | // Find a validation with our configuration | ||
445 | var result *shadowResourceProviderValidateResource | ||
446 | for { | ||
447 | // Get the value | ||
448 | if raw == nil { | ||
449 | p.ErrorLock.Lock() | ||
450 | defer p.ErrorLock.Unlock() | ||
451 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
452 | "Unknown 'ValidateResource' call for %q:\n\n%#v", | ||
453 | key, c)) | ||
454 | return nil, nil | ||
455 | } | ||
456 | |||
457 | wrapper, ok := raw.(*shadowResourceProviderValidateResourceWrapper) | ||
458 | if !ok { | ||
459 | p.ErrorLock.Lock() | ||
460 | defer p.ErrorLock.Unlock() | ||
461 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
462 | "Unknown 'ValidateResource' shadow value for %q: %#v", key, raw)) | ||
463 | return nil, nil | ||
464 | } | ||
465 | |||
466 | // Look for the matching call with our configuration | ||
467 | wrapper.RLock() | ||
468 | for _, call := range wrapper.Calls { | ||
469 | if call.Config.Equal(c) { | ||
470 | result = call | ||
471 | break | ||
472 | } | ||
473 | } | ||
474 | wrapper.RUnlock() | ||
475 | |||
476 | // If we found a result, exit | ||
477 | if result != nil { | ||
478 | break | ||
479 | } | ||
480 | |||
481 | // Wait for a change so we can get the wrapper again | ||
482 | raw = p.Shared.ValidateResource.WaitForChange(key) | ||
483 | } | ||
484 | |||
485 | return result.Warns, result.Errors | ||
486 | } | ||
487 | |||
488 | func (p *shadowResourceProviderShadow) Apply( | ||
489 | info *InstanceInfo, | ||
490 | state *InstanceState, | ||
491 | diff *InstanceDiff) (*InstanceState, error) { | ||
492 | // Unique key | ||
493 | key := info.uniqueId() | ||
494 | raw := p.Shared.Apply.Value(key) | ||
495 | if raw == nil { | ||
496 | p.ErrorLock.Lock() | ||
497 | defer p.ErrorLock.Unlock() | ||
498 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
499 | "Unknown 'apply' call for %q:\n\n%#v\n\n%#v", | ||
500 | key, state, diff)) | ||
501 | return nil, nil | ||
502 | } | ||
503 | |||
504 | result, ok := raw.(*shadowResourceProviderApply) | ||
505 | if !ok { | ||
506 | p.ErrorLock.Lock() | ||
507 | defer p.ErrorLock.Unlock() | ||
508 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
509 | "Unknown 'apply' shadow value for %q: %#v", key, raw)) | ||
510 | return nil, nil | ||
511 | } | ||
512 | |||
513 | // Compare the parameters, which should be identical | ||
514 | if !state.Equal(result.State) { | ||
515 | p.ErrorLock.Lock() | ||
516 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
517 | "Apply %q: state had unequal states (real, then shadow):\n\n%#v\n\n%#v", | ||
518 | key, result.State, state)) | ||
519 | p.ErrorLock.Unlock() | ||
520 | } | ||
521 | |||
522 | if !diff.Equal(result.Diff) { | ||
523 | p.ErrorLock.Lock() | ||
524 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
525 | "Apply %q: unequal diffs (real, then shadow):\n\n%#v\n\n%#v", | ||
526 | key, result.Diff, diff)) | ||
527 | p.ErrorLock.Unlock() | ||
528 | } | ||
529 | |||
530 | return result.Result, result.ResultErr | ||
531 | } | ||
532 | |||
533 | func (p *shadowResourceProviderShadow) Diff( | ||
534 | info *InstanceInfo, | ||
535 | state *InstanceState, | ||
536 | desired *ResourceConfig) (*InstanceDiff, error) { | ||
537 | // Unique key | ||
538 | key := info.uniqueId() | ||
539 | raw := p.Shared.Diff.Value(key) | ||
540 | if raw == nil { | ||
541 | p.ErrorLock.Lock() | ||
542 | defer p.ErrorLock.Unlock() | ||
543 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
544 | "Unknown 'diff' call for %q:\n\n%#v\n\n%#v", | ||
545 | key, state, desired)) | ||
546 | return nil, nil | ||
547 | } | ||
548 | |||
549 | result, ok := raw.(*shadowResourceProviderDiff) | ||
550 | if !ok { | ||
551 | p.ErrorLock.Lock() | ||
552 | defer p.ErrorLock.Unlock() | ||
553 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
554 | "Unknown 'diff' shadow value for %q: %#v", key, raw)) | ||
555 | return nil, nil | ||
556 | } | ||
557 | |||
558 | // Compare the parameters, which should be identical | ||
559 | if !state.Equal(result.State) { | ||
560 | p.ErrorLock.Lock() | ||
561 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
562 | "Diff %q had unequal states (real, then shadow):\n\n%#v\n\n%#v", | ||
563 | key, result.State, state)) | ||
564 | p.ErrorLock.Unlock() | ||
565 | } | ||
566 | if !desired.Equal(result.Desired) { | ||
567 | p.ErrorLock.Lock() | ||
568 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
569 | "Diff %q had unequal states (real, then shadow):\n\n%#v\n\n%#v", | ||
570 | key, result.Desired, desired)) | ||
571 | p.ErrorLock.Unlock() | ||
572 | } | ||
573 | |||
574 | return result.Result, result.ResultErr | ||
575 | } | ||
576 | |||
577 | func (p *shadowResourceProviderShadow) Refresh( | ||
578 | info *InstanceInfo, | ||
579 | state *InstanceState) (*InstanceState, error) { | ||
580 | // Unique key | ||
581 | key := info.uniqueId() | ||
582 | raw := p.Shared.Refresh.Value(key) | ||
583 | if raw == nil { | ||
584 | p.ErrorLock.Lock() | ||
585 | defer p.ErrorLock.Unlock() | ||
586 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
587 | "Unknown 'refresh' call for %q:\n\n%#v", | ||
588 | key, state)) | ||
589 | return nil, nil | ||
590 | } | ||
591 | |||
592 | result, ok := raw.(*shadowResourceProviderRefresh) | ||
593 | if !ok { | ||
594 | p.ErrorLock.Lock() | ||
595 | defer p.ErrorLock.Unlock() | ||
596 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
597 | "Unknown 'refresh' shadow value: %#v", raw)) | ||
598 | return nil, nil | ||
599 | } | ||
600 | |||
601 | // Compare the parameters, which should be identical | ||
602 | if !state.Equal(result.State) { | ||
603 | p.ErrorLock.Lock() | ||
604 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
605 | "Refresh %q had unequal states (real, then shadow):\n\n%#v\n\n%#v", | ||
606 | key, result.State, state)) | ||
607 | p.ErrorLock.Unlock() | ||
608 | } | ||
609 | |||
610 | return result.Result, result.ResultErr | ||
611 | } | ||
612 | |||
613 | func (p *shadowResourceProviderShadow) ValidateDataSource( | ||
614 | t string, c *ResourceConfig) ([]string, []error) { | ||
615 | // Unique key | ||
616 | key := t | ||
617 | |||
618 | // Get the initial value | ||
619 | raw := p.Shared.ValidateDataSource.Value(key) | ||
620 | |||
621 | // Find a validation with our configuration | ||
622 | var result *shadowResourceProviderValidateDataSource | ||
623 | for { | ||
624 | // Get the value | ||
625 | if raw == nil { | ||
626 | p.ErrorLock.Lock() | ||
627 | defer p.ErrorLock.Unlock() | ||
628 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
629 | "Unknown 'ValidateDataSource' call for %q:\n\n%#v", | ||
630 | key, c)) | ||
631 | return nil, nil | ||
632 | } | ||
633 | |||
634 | wrapper, ok := raw.(*shadowResourceProviderValidateDataSourceWrapper) | ||
635 | if !ok { | ||
636 | p.ErrorLock.Lock() | ||
637 | defer p.ErrorLock.Unlock() | ||
638 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
639 | "Unknown 'ValidateDataSource' shadow value: %#v", raw)) | ||
640 | return nil, nil | ||
641 | } | ||
642 | |||
643 | // Look for the matching call with our configuration | ||
644 | wrapper.RLock() | ||
645 | for _, call := range wrapper.Calls { | ||
646 | if call.Config.Equal(c) { | ||
647 | result = call | ||
648 | break | ||
649 | } | ||
650 | } | ||
651 | wrapper.RUnlock() | ||
652 | |||
653 | // If we found a result, exit | ||
654 | if result != nil { | ||
655 | break | ||
656 | } | ||
657 | |||
658 | // Wait for a change so we can get the wrapper again | ||
659 | raw = p.Shared.ValidateDataSource.WaitForChange(key) | ||
660 | } | ||
661 | |||
662 | return result.Warns, result.Errors | ||
663 | } | ||
664 | |||
665 | func (p *shadowResourceProviderShadow) ReadDataDiff( | ||
666 | info *InstanceInfo, | ||
667 | desired *ResourceConfig) (*InstanceDiff, error) { | ||
668 | // Unique key | ||
669 | key := info.uniqueId() | ||
670 | raw := p.Shared.ReadDataDiff.Value(key) | ||
671 | if raw == nil { | ||
672 | p.ErrorLock.Lock() | ||
673 | defer p.ErrorLock.Unlock() | ||
674 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
675 | "Unknown 'ReadDataDiff' call for %q:\n\n%#v", | ||
676 | key, desired)) | ||
677 | return nil, nil | ||
678 | } | ||
679 | |||
680 | result, ok := raw.(*shadowResourceProviderReadDataDiff) | ||
681 | if !ok { | ||
682 | p.ErrorLock.Lock() | ||
683 | defer p.ErrorLock.Unlock() | ||
684 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
685 | "Unknown 'ReadDataDiff' shadow value for %q: %#v", key, raw)) | ||
686 | return nil, nil | ||
687 | } | ||
688 | |||
689 | // Compare the parameters, which should be identical | ||
690 | if !desired.Equal(result.Desired) { | ||
691 | p.ErrorLock.Lock() | ||
692 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
693 | "ReadDataDiff %q had unequal configs (real, then shadow):\n\n%#v\n\n%#v", | ||
694 | key, result.Desired, desired)) | ||
695 | p.ErrorLock.Unlock() | ||
696 | } | ||
697 | |||
698 | return result.Result, result.ResultErr | ||
699 | } | ||
700 | |||
701 | func (p *shadowResourceProviderShadow) ReadDataApply( | ||
702 | info *InstanceInfo, | ||
703 | d *InstanceDiff) (*InstanceState, error) { | ||
704 | // Unique key | ||
705 | key := info.uniqueId() | ||
706 | raw := p.Shared.ReadDataApply.Value(key) | ||
707 | if raw == nil { | ||
708 | p.ErrorLock.Lock() | ||
709 | defer p.ErrorLock.Unlock() | ||
710 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
711 | "Unknown 'ReadDataApply' call for %q:\n\n%#v", | ||
712 | key, d)) | ||
713 | return nil, nil | ||
714 | } | ||
715 | |||
716 | result, ok := raw.(*shadowResourceProviderReadDataApply) | ||
717 | if !ok { | ||
718 | p.ErrorLock.Lock() | ||
719 | defer p.ErrorLock.Unlock() | ||
720 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
721 | "Unknown 'ReadDataApply' shadow value for %q: %#v", key, raw)) | ||
722 | return nil, nil | ||
723 | } | ||
724 | |||
725 | // Compare the parameters, which should be identical | ||
726 | if !d.Equal(result.Diff) { | ||
727 | p.ErrorLock.Lock() | ||
728 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
729 | "ReadDataApply: unequal diffs (real, then shadow):\n\n%#v\n\n%#v", | ||
730 | result.Diff, d)) | ||
731 | p.ErrorLock.Unlock() | ||
732 | } | ||
733 | |||
734 | return result.Result, result.ResultErr | ||
735 | } | ||
736 | |||
737 | func (p *shadowResourceProviderShadow) ImportState(info *InstanceInfo, id string) ([]*InstanceState, error) { | ||
738 | panic("import not supported by shadow graph") | ||
739 | } | ||
740 | |||
741 | // The structs for the various function calls are put below. These structs | ||
742 | // are used to carry call information across the real/shadow boundaries. | ||
743 | |||
744 | type shadowResourceProviderInput struct { | ||
745 | Config *ResourceConfig | ||
746 | Result *ResourceConfig | ||
747 | ResultErr error | ||
748 | } | ||
749 | |||
750 | type shadowResourceProviderValidate struct { | ||
751 | Config *ResourceConfig | ||
752 | ResultWarn []string | ||
753 | ResultErr []error | ||
754 | } | ||
755 | |||
756 | type shadowResourceProviderConfigure struct { | ||
757 | Config *ResourceConfig | ||
758 | Result error | ||
759 | } | ||
760 | |||
761 | type shadowResourceProviderValidateResourceWrapper struct { | ||
762 | sync.RWMutex | ||
763 | |||
764 | Calls []*shadowResourceProviderValidateResource | ||
765 | } | ||
766 | |||
767 | type shadowResourceProviderValidateResource struct { | ||
768 | Config *ResourceConfig | ||
769 | Warns []string | ||
770 | Errors []error | ||
771 | } | ||
772 | |||
773 | type shadowResourceProviderApply struct { | ||
774 | State *InstanceState | ||
775 | Diff *InstanceDiff | ||
776 | Result *InstanceState | ||
777 | ResultErr error | ||
778 | } | ||
779 | |||
780 | type shadowResourceProviderDiff struct { | ||
781 | State *InstanceState | ||
782 | Desired *ResourceConfig | ||
783 | Result *InstanceDiff | ||
784 | ResultErr error | ||
785 | } | ||
786 | |||
787 | type shadowResourceProviderRefresh struct { | ||
788 | State *InstanceState | ||
789 | Result *InstanceState | ||
790 | ResultErr error | ||
791 | } | ||
792 | |||
793 | type shadowResourceProviderValidateDataSourceWrapper struct { | ||
794 | sync.RWMutex | ||
795 | |||
796 | Calls []*shadowResourceProviderValidateDataSource | ||
797 | } | ||
798 | |||
799 | type shadowResourceProviderValidateDataSource struct { | ||
800 | Config *ResourceConfig | ||
801 | Warns []string | ||
802 | Errors []error | ||
803 | } | ||
804 | |||
805 | type shadowResourceProviderReadDataDiff struct { | ||
806 | Desired *ResourceConfig | ||
807 | Result *InstanceDiff | ||
808 | ResultErr error | ||
809 | } | ||
810 | |||
811 | type shadowResourceProviderReadDataApply struct { | ||
812 | Diff *InstanceDiff | ||
813 | Result *InstanceState | ||
814 | ResultErr error | ||
815 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provisioner.go b/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provisioner.go deleted file mode 100644 index 60a4908..0000000 --- a/vendor/github.com/hashicorp/terraform/terraform/shadow_resource_provisioner.go +++ /dev/null | |||
@@ -1,282 +0,0 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "io" | ||
6 | "log" | ||
7 | "sync" | ||
8 | |||
9 | "github.com/hashicorp/go-multierror" | ||
10 | "github.com/hashicorp/terraform/helper/shadow" | ||
11 | ) | ||
12 | |||
13 | // shadowResourceProvisioner implements ResourceProvisioner for the shadow | ||
14 | // eval context defined in eval_context_shadow.go. | ||
15 | // | ||
16 | // This is used to verify behavior with a real provisioner. This shouldn't | ||
17 | // be used directly. | ||
18 | type shadowResourceProvisioner interface { | ||
19 | ResourceProvisioner | ||
20 | Shadow | ||
21 | } | ||
22 | |||
23 | // newShadowResourceProvisioner creates a new shadowed ResourceProvisioner. | ||
24 | func newShadowResourceProvisioner( | ||
25 | p ResourceProvisioner) (ResourceProvisioner, shadowResourceProvisioner) { | ||
26 | // Create the shared data | ||
27 | shared := shadowResourceProvisionerShared{ | ||
28 | Validate: shadow.ComparedValue{ | ||
29 | Func: shadowResourceProvisionerValidateCompare, | ||
30 | }, | ||
31 | } | ||
32 | |||
33 | // Create the real provisioner that does actual work | ||
34 | real := &shadowResourceProvisionerReal{ | ||
35 | ResourceProvisioner: p, | ||
36 | Shared: &shared, | ||
37 | } | ||
38 | |||
39 | // Create the shadow that watches the real value | ||
40 | shadow := &shadowResourceProvisionerShadow{ | ||
41 | Shared: &shared, | ||
42 | } | ||
43 | |||
44 | return real, shadow | ||
45 | } | ||
46 | |||
47 | // shadowResourceProvisionerReal is the real resource provisioner. Function calls | ||
48 | // to this will perform real work. This records the parameters and return | ||
49 | // values and call order for the shadow to reproduce. | ||
50 | type shadowResourceProvisionerReal struct { | ||
51 | ResourceProvisioner | ||
52 | |||
53 | Shared *shadowResourceProvisionerShared | ||
54 | } | ||
55 | |||
56 | func (p *shadowResourceProvisionerReal) Close() error { | ||
57 | var result error | ||
58 | if c, ok := p.ResourceProvisioner.(ResourceProvisionerCloser); ok { | ||
59 | result = c.Close() | ||
60 | } | ||
61 | |||
62 | p.Shared.CloseErr.SetValue(result) | ||
63 | return result | ||
64 | } | ||
65 | |||
66 | func (p *shadowResourceProvisionerReal) Validate(c *ResourceConfig) ([]string, []error) { | ||
67 | warns, errs := p.ResourceProvisioner.Validate(c) | ||
68 | p.Shared.Validate.SetValue(&shadowResourceProvisionerValidate{ | ||
69 | Config: c, | ||
70 | ResultWarn: warns, | ||
71 | ResultErr: errs, | ||
72 | }) | ||
73 | |||
74 | return warns, errs | ||
75 | } | ||
76 | |||
77 | func (p *shadowResourceProvisionerReal) Apply( | ||
78 | output UIOutput, s *InstanceState, c *ResourceConfig) error { | ||
79 | err := p.ResourceProvisioner.Apply(output, s, c) | ||
80 | |||
81 | // Write the result, grab a lock for writing. This should nver | ||
82 | // block long since the operations below don't block. | ||
83 | p.Shared.ApplyLock.Lock() | ||
84 | defer p.Shared.ApplyLock.Unlock() | ||
85 | |||
86 | key := s.ID | ||
87 | raw, ok := p.Shared.Apply.ValueOk(key) | ||
88 | if !ok { | ||
89 | // Setup a new value | ||
90 | raw = &shadow.ComparedValue{ | ||
91 | Func: shadowResourceProvisionerApplyCompare, | ||
92 | } | ||
93 | |||
94 | // Set it | ||
95 | p.Shared.Apply.SetValue(key, raw) | ||
96 | } | ||
97 | |||
98 | compareVal, ok := raw.(*shadow.ComparedValue) | ||
99 | if !ok { | ||
100 | // Just log and return so that we don't cause the real side | ||
101 | // any side effects. | ||
102 | log.Printf("[ERROR] unknown value in 'apply': %#v", raw) | ||
103 | return err | ||
104 | } | ||
105 | |||
106 | // Write the resulting value | ||
107 | compareVal.SetValue(&shadowResourceProvisionerApply{ | ||
108 | Config: c, | ||
109 | ResultErr: err, | ||
110 | }) | ||
111 | |||
112 | return err | ||
113 | } | ||
114 | |||
115 | func (p *shadowResourceProvisionerReal) Stop() error { | ||
116 | return p.ResourceProvisioner.Stop() | ||
117 | } | ||
118 | |||
119 | // shadowResourceProvisionerShadow is the shadow resource provisioner. Function | ||
120 | // calls never affect real resources. This is paired with the "real" side | ||
121 | // which must be called properly to enable recording. | ||
122 | type shadowResourceProvisionerShadow struct { | ||
123 | Shared *shadowResourceProvisionerShared | ||
124 | |||
125 | Error error // Error is the list of errors from the shadow | ||
126 | ErrorLock sync.Mutex | ||
127 | } | ||
128 | |||
129 | type shadowResourceProvisionerShared struct { | ||
130 | // NOTE: Anytime a value is added here, be sure to add it to | ||
131 | // the Close() method so that it is closed. | ||
132 | |||
133 | CloseErr shadow.Value | ||
134 | Validate shadow.ComparedValue | ||
135 | Apply shadow.KeyedValue | ||
136 | ApplyLock sync.Mutex // For writing only | ||
137 | } | ||
138 | |||
139 | func (p *shadowResourceProvisionerShared) Close() error { | ||
140 | closers := []io.Closer{ | ||
141 | &p.CloseErr, | ||
142 | } | ||
143 | |||
144 | for _, c := range closers { | ||
145 | // This should never happen, but we don't panic because a panic | ||
146 | // could affect the real behavior of Terraform and a shadow should | ||
147 | // never be able to do that. | ||
148 | if err := c.Close(); err != nil { | ||
149 | return err | ||
150 | } | ||
151 | } | ||
152 | |||
153 | return nil | ||
154 | } | ||
155 | |||
156 | func (p *shadowResourceProvisionerShadow) CloseShadow() error { | ||
157 | err := p.Shared.Close() | ||
158 | if err != nil { | ||
159 | err = fmt.Errorf("close error: %s", err) | ||
160 | } | ||
161 | |||
162 | return err | ||
163 | } | ||
164 | |||
165 | func (p *shadowResourceProvisionerShadow) ShadowError() error { | ||
166 | return p.Error | ||
167 | } | ||
168 | |||
169 | func (p *shadowResourceProvisionerShadow) Close() error { | ||
170 | v := p.Shared.CloseErr.Value() | ||
171 | if v == nil { | ||
172 | return nil | ||
173 | } | ||
174 | |||
175 | return v.(error) | ||
176 | } | ||
177 | |||
178 | func (p *shadowResourceProvisionerShadow) Validate(c *ResourceConfig) ([]string, []error) { | ||
179 | // Get the result of the validate call | ||
180 | raw := p.Shared.Validate.Value(c) | ||
181 | if raw == nil { | ||
182 | return nil, nil | ||
183 | } | ||
184 | |||
185 | result, ok := raw.(*shadowResourceProvisionerValidate) | ||
186 | if !ok { | ||
187 | p.ErrorLock.Lock() | ||
188 | defer p.ErrorLock.Unlock() | ||
189 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
190 | "Unknown 'validate' shadow value: %#v", raw)) | ||
191 | return nil, nil | ||
192 | } | ||
193 | |||
194 | // We don't need to compare configurations because we key on the | ||
195 | // configuration so just return right away. | ||
196 | return result.ResultWarn, result.ResultErr | ||
197 | } | ||
198 | |||
199 | func (p *shadowResourceProvisionerShadow) Apply( | ||
200 | output UIOutput, s *InstanceState, c *ResourceConfig) error { | ||
201 | // Get the value based on the key | ||
202 | key := s.ID | ||
203 | raw := p.Shared.Apply.Value(key) | ||
204 | if raw == nil { | ||
205 | return nil | ||
206 | } | ||
207 | |||
208 | compareVal, ok := raw.(*shadow.ComparedValue) | ||
209 | if !ok { | ||
210 | p.ErrorLock.Lock() | ||
211 | defer p.ErrorLock.Unlock() | ||
212 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
213 | "Unknown 'apply' shadow value: %#v", raw)) | ||
214 | return nil | ||
215 | } | ||
216 | |||
217 | // With the compared value, we compare against our config | ||
218 | raw = compareVal.Value(c) | ||
219 | if raw == nil { | ||
220 | return nil | ||
221 | } | ||
222 | |||
223 | result, ok := raw.(*shadowResourceProvisionerApply) | ||
224 | if !ok { | ||
225 | p.ErrorLock.Lock() | ||
226 | defer p.ErrorLock.Unlock() | ||
227 | p.Error = multierror.Append(p.Error, fmt.Errorf( | ||
228 | "Unknown 'apply' shadow value: %#v", raw)) | ||
229 | return nil | ||
230 | } | ||
231 | |||
232 | return result.ResultErr | ||
233 | } | ||
234 | |||
235 | func (p *shadowResourceProvisionerShadow) Stop() error { | ||
236 | // For the shadow, we always just return nil since a Stop indicates | ||
237 | // that we were interrupted and shadows are disabled during interrupts | ||
238 | // anyways. | ||
239 | return nil | ||
240 | } | ||
241 | |||
242 | // The structs for the various function calls are put below. These structs | ||
243 | // are used to carry call information across the real/shadow boundaries. | ||
244 | |||
245 | type shadowResourceProvisionerValidate struct { | ||
246 | Config *ResourceConfig | ||
247 | ResultWarn []string | ||
248 | ResultErr []error | ||
249 | } | ||
250 | |||
251 | type shadowResourceProvisionerApply struct { | ||
252 | Config *ResourceConfig | ||
253 | ResultErr error | ||
254 | } | ||
255 | |||
256 | func shadowResourceProvisionerValidateCompare(k, v interface{}) bool { | ||
257 | c, ok := k.(*ResourceConfig) | ||
258 | if !ok { | ||
259 | return false | ||
260 | } | ||
261 | |||
262 | result, ok := v.(*shadowResourceProvisionerValidate) | ||
263 | if !ok { | ||
264 | return false | ||
265 | } | ||
266 | |||
267 | return c.Equal(result.Config) | ||
268 | } | ||
269 | |||
270 | func shadowResourceProvisionerApplyCompare(k, v interface{}) bool { | ||
271 | c, ok := k.(*ResourceConfig) | ||
272 | if !ok { | ||
273 | return false | ||
274 | } | ||
275 | |||
276 | result, ok := v.(*shadowResourceProvisionerApply) | ||
277 | if !ok { | ||
278 | return false | ||
279 | } | ||
280 | |||
281 | return c.Equal(result.Config) | ||
282 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/state.go b/vendor/github.com/hashicorp/terraform/terraform/state.go index 0c46194..04b14a6 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/state.go +++ b/vendor/github.com/hashicorp/terraform/terraform/state.go | |||
@@ -9,6 +9,7 @@ import ( | |||
9 | "io" | 9 | "io" |
10 | "io/ioutil" | 10 | "io/ioutil" |
11 | "log" | 11 | "log" |
12 | "os" | ||
12 | "reflect" | 13 | "reflect" |
13 | "sort" | 14 | "sort" |
14 | "strconv" | 15 | "strconv" |
@@ -16,10 +17,12 @@ import ( | |||
16 | "sync" | 17 | "sync" |
17 | 18 | ||
18 | "github.com/hashicorp/go-multierror" | 19 | "github.com/hashicorp/go-multierror" |
20 | "github.com/hashicorp/go-uuid" | ||
19 | "github.com/hashicorp/go-version" | 21 | "github.com/hashicorp/go-version" |
20 | "github.com/hashicorp/terraform/config" | 22 | "github.com/hashicorp/terraform/config" |
21 | "github.com/mitchellh/copystructure" | 23 | "github.com/mitchellh/copystructure" |
22 | "github.com/satori/go.uuid" | 24 | |
25 | tfversion "github.com/hashicorp/terraform/version" | ||
23 | ) | 26 | ) |
24 | 27 | ||
25 | const ( | 28 | const ( |
@@ -664,7 +667,7 @@ func (s *State) FromFutureTerraform() bool { | |||
664 | } | 667 | } |
665 | 668 | ||
666 | v := version.Must(version.NewVersion(s.TFVersion)) | 669 | v := version.Must(version.NewVersion(s.TFVersion)) |
667 | return SemVersion.LessThan(v) | 670 | return tfversion.SemVer.LessThan(v) |
668 | } | 671 | } |
669 | 672 | ||
670 | func (s *State) Init() { | 673 | func (s *State) Init() { |
@@ -704,7 +707,11 @@ func (s *State) EnsureHasLineage() { | |||
704 | 707 | ||
705 | func (s *State) ensureHasLineage() { | 708 | func (s *State) ensureHasLineage() { |
706 | if s.Lineage == "" { | 709 | if s.Lineage == "" { |
707 | s.Lineage = uuid.NewV4().String() | 710 | lineage, err := uuid.GenerateUUID() |
711 | if err != nil { | ||
712 | panic(fmt.Errorf("Failed to generate lineage: %v", err)) | ||
713 | } | ||
714 | s.Lineage = lineage | ||
708 | log.Printf("[DEBUG] New state was assigned lineage %q\n", s.Lineage) | 715 | log.Printf("[DEBUG] New state was assigned lineage %q\n", s.Lineage) |
709 | } else { | 716 | } else { |
710 | log.Printf("[TRACE] Preserving existing state lineage %q\n", s.Lineage) | 717 | log.Printf("[TRACE] Preserving existing state lineage %q\n", s.Lineage) |
@@ -977,6 +984,10 @@ type ModuleState struct { | |||
977 | // always disjoint, so the path represents amodule tree | 984 | // always disjoint, so the path represents amodule tree |
978 | Path []string `json:"path"` | 985 | Path []string `json:"path"` |
979 | 986 | ||
987 | // Locals are kept only transiently in-memory, because we can always | ||
988 | // re-compute them. | ||
989 | Locals map[string]interface{} `json:"-"` | ||
990 | |||
980 | // Outputs declared by the module and maintained for each module | 991 | // Outputs declared by the module and maintained for each module |
981 | // even though only the root module technically needs to be kept. | 992 | // even though only the root module technically needs to be kept. |
982 | // This allows operators to inspect values at the boundaries. | 993 | // This allows operators to inspect values at the boundaries. |
@@ -1083,7 +1094,7 @@ func (m *ModuleState) Orphans(c *config.Config) []string { | |||
1083 | defer m.Unlock() | 1094 | defer m.Unlock() |
1084 | 1095 | ||
1085 | keys := make(map[string]struct{}) | 1096 | keys := make(map[string]struct{}) |
1086 | for k, _ := range m.Resources { | 1097 | for k := range m.Resources { |
1087 | keys[k] = struct{}{} | 1098 | keys[k] = struct{}{} |
1088 | } | 1099 | } |
1089 | 1100 | ||
@@ -1091,7 +1102,7 @@ func (m *ModuleState) Orphans(c *config.Config) []string { | |||
1091 | for _, r := range c.Resources { | 1102 | for _, r := range c.Resources { |
1092 | delete(keys, r.Id()) | 1103 | delete(keys, r.Id()) |
1093 | 1104 | ||
1094 | for k, _ := range keys { | 1105 | for k := range keys { |
1095 | if strings.HasPrefix(k, r.Id()+".") { | 1106 | if strings.HasPrefix(k, r.Id()+".") { |
1096 | delete(keys, k) | 1107 | delete(keys, k) |
1097 | } | 1108 | } |
@@ -1100,7 +1111,32 @@ func (m *ModuleState) Orphans(c *config.Config) []string { | |||
1100 | } | 1111 | } |
1101 | 1112 | ||
1102 | result := make([]string, 0, len(keys)) | 1113 | result := make([]string, 0, len(keys)) |
1103 | for k, _ := range keys { | 1114 | for k := range keys { |
1115 | result = append(result, k) | ||
1116 | } | ||
1117 | |||
1118 | return result | ||
1119 | } | ||
1120 | |||
1121 | // RemovedOutputs returns a list of outputs that are in the State but aren't | ||
1122 | // present in the configuration itself. | ||
1123 | func (m *ModuleState) RemovedOutputs(c *config.Config) []string { | ||
1124 | m.Lock() | ||
1125 | defer m.Unlock() | ||
1126 | |||
1127 | keys := make(map[string]struct{}) | ||
1128 | for k := range m.Outputs { | ||
1129 | keys[k] = struct{}{} | ||
1130 | } | ||
1131 | |||
1132 | if c != nil { | ||
1133 | for _, o := range c.Outputs { | ||
1134 | delete(keys, o.Name) | ||
1135 | } | ||
1136 | } | ||
1137 | |||
1138 | result := make([]string, 0, len(keys)) | ||
1139 | for k := range keys { | ||
1104 | result = append(result, k) | 1140 | result = append(result, k) |
1105 | } | 1141 | } |
1106 | 1142 | ||
@@ -1308,6 +1344,10 @@ func (m *ModuleState) String() string { | |||
1308 | return buf.String() | 1344 | return buf.String() |
1309 | } | 1345 | } |
1310 | 1346 | ||
1347 | func (m *ModuleState) Empty() bool { | ||
1348 | return len(m.Locals) == 0 && len(m.Outputs) == 0 && len(m.Resources) == 0 | ||
1349 | } | ||
1350 | |||
1311 | // ResourceStateKey is a structured representation of the key used for the | 1351 | // ResourceStateKey is a structured representation of the key used for the |
1312 | // ModuleState.Resources mapping | 1352 | // ModuleState.Resources mapping |
1313 | type ResourceStateKey struct { | 1353 | type ResourceStateKey struct { |
@@ -1681,7 +1721,20 @@ func (s *InstanceState) Equal(other *InstanceState) bool { | |||
1681 | // We only do the deep check if both are non-nil. If one is nil | 1721 | // We only do the deep check if both are non-nil. If one is nil |
1682 | // we treat it as equal since their lengths are both zero (check | 1722 | // we treat it as equal since their lengths are both zero (check |
1683 | // above). | 1723 | // above). |
1684 | if !reflect.DeepEqual(s.Meta, other.Meta) { | 1724 | // |
1725 | // Since this can contain numeric values that may change types during | ||
1726 | // serialization, let's compare the serialized values. | ||
1727 | sMeta, err := json.Marshal(s.Meta) | ||
1728 | if err != nil { | ||
1729 | // marshaling primitives shouldn't ever error out | ||
1730 | panic(err) | ||
1731 | } | ||
1732 | otherMeta, err := json.Marshal(other.Meta) | ||
1733 | if err != nil { | ||
1734 | panic(err) | ||
1735 | } | ||
1736 | |||
1737 | if !bytes.Equal(sMeta, otherMeta) { | ||
1685 | return false | 1738 | return false |
1686 | } | 1739 | } |
1687 | } | 1740 | } |
@@ -1824,11 +1877,19 @@ var ErrNoState = errors.New("no state") | |||
1824 | // ReadState reads a state structure out of a reader in the format that | 1877 | // ReadState reads a state structure out of a reader in the format that |
1825 | // was written by WriteState. | 1878 | // was written by WriteState. |
1826 | func ReadState(src io.Reader) (*State, error) { | 1879 | func ReadState(src io.Reader) (*State, error) { |
1880 | // check for a nil file specifically, since that produces a platform | ||
1881 | // specific error if we try to use it in a bufio.Reader. | ||
1882 | if f, ok := src.(*os.File); ok && f == nil { | ||
1883 | return nil, ErrNoState | ||
1884 | } | ||
1885 | |||
1827 | buf := bufio.NewReader(src) | 1886 | buf := bufio.NewReader(src) |
1887 | |||
1828 | if _, err := buf.Peek(1); err != nil { | 1888 | if _, err := buf.Peek(1); err != nil { |
1829 | // the error is either io.EOF or "invalid argument", and both are from | 1889 | if err == io.EOF { |
1830 | // an empty state. | 1890 | return nil, ErrNoState |
1831 | return nil, ErrNoState | 1891 | } |
1892 | return nil, err | ||
1832 | } | 1893 | } |
1833 | 1894 | ||
1834 | if err := testForV0State(buf); err != nil { | 1895 | if err := testForV0State(buf); err != nil { |
@@ -1891,7 +1952,7 @@ func ReadState(src io.Reader) (*State, error) { | |||
1891 | result = v3State | 1952 | result = v3State |
1892 | default: | 1953 | default: |
1893 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", | 1954 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", |
1894 | SemVersion.String(), versionIdentifier.Version) | 1955 | tfversion.SemVer.String(), versionIdentifier.Version) |
1895 | } | 1956 | } |
1896 | 1957 | ||
1897 | // If we reached this place we must have a result set | 1958 | // If we reached this place we must have a result set |
@@ -1935,7 +1996,7 @@ func ReadStateV2(jsonBytes []byte) (*State, error) { | |||
1935 | // version that we don't understand | 1996 | // version that we don't understand |
1936 | if state.Version > StateVersion { | 1997 | if state.Version > StateVersion { |
1937 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", | 1998 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", |
1938 | SemVersion.String(), state.Version) | 1999 | tfversion.SemVer.String(), state.Version) |
1939 | } | 2000 | } |
1940 | 2001 | ||
1941 | // Make sure the version is semantic | 2002 | // Make sure the version is semantic |
@@ -1970,7 +2031,7 @@ func ReadStateV3(jsonBytes []byte) (*State, error) { | |||
1970 | // version that we don't understand | 2031 | // version that we don't understand |
1971 | if state.Version > StateVersion { | 2032 | if state.Version > StateVersion { |
1972 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", | 2033 | return nil, fmt.Errorf("Terraform %s does not support state version %d, please update.", |
1973 | SemVersion.String(), state.Version) | 2034 | tfversion.SemVer.String(), state.Version) |
1974 | } | 2035 | } |
1975 | 2036 | ||
1976 | // Make sure the version is semantic | 2037 | // Make sure the version is semantic |
@@ -2126,6 +2187,19 @@ func (s moduleStateSort) Swap(i, j int) { | |||
2126 | s[i], s[j] = s[j], s[i] | 2187 | s[i], s[j] = s[j], s[i] |
2127 | } | 2188 | } |
2128 | 2189 | ||
2190 | // StateCompatible returns an error if the state is not compatible with the | ||
2191 | // current version of terraform. | ||
2192 | func CheckStateVersion(state *State) error { | ||
2193 | if state == nil { | ||
2194 | return nil | ||
2195 | } | ||
2196 | |||
2197 | if state.FromFutureTerraform() { | ||
2198 | return fmt.Errorf(stateInvalidTerraformVersionErr, state.TFVersion) | ||
2199 | } | ||
2200 | return nil | ||
2201 | } | ||
2202 | |||
2129 | const stateValidateErrMultiModule = ` | 2203 | const stateValidateErrMultiModule = ` |
2130 | Multiple modules with the same path: %s | 2204 | Multiple modules with the same path: %s |
2131 | 2205 | ||
@@ -2134,3 +2208,11 @@ in your state file that point to the same module. This will cause Terraform | |||
2134 | to behave in unexpected and error prone ways and is invalid. Please back up | 2208 | to behave in unexpected and error prone ways and is invalid. Please back up |
2135 | and modify your state file manually to resolve this. | 2209 | and modify your state file manually to resolve this. |
2136 | ` | 2210 | ` |
2211 | |||
2212 | const stateInvalidTerraformVersionErr = ` | ||
2213 | Terraform doesn't allow running any operations against a state | ||
2214 | that was written by a future Terraform version. The state is | ||
2215 | reporting it is written by Terraform '%s' | ||
2216 | |||
2217 | Please run at least that version of Terraform to continue. | ||
2218 | ` | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/test_failure b/vendor/github.com/hashicorp/terraform/terraform/test_failure deleted file mode 100644 index 5d3ad1a..0000000 --- a/vendor/github.com/hashicorp/terraform/terraform/test_failure +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | --- FAIL: TestContext2Plan_moduleProviderInherit (0.01s) | ||
2 | context_plan_test.go:552: bad: []string{"child"} | ||
3 | map[string]dag.Vertex{} | ||
4 | "module.middle.null" | ||
5 | map[string]dag.Vertex{} | ||
6 | "module.middle.module.inner.null" | ||
7 | map[string]dag.Vertex{} | ||
8 | "aws" | ||
9 | FAIL | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform.go b/vendor/github.com/hashicorp/terraform/terraform/transform.go index f4a431a..0e47f20 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform.go | |||
@@ -1,6 +1,8 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "log" | ||
5 | |||
4 | "github.com/hashicorp/terraform/dag" | 6 | "github.com/hashicorp/terraform/dag" |
5 | ) | 7 | ) |
6 | 8 | ||
@@ -40,6 +42,9 @@ func (t *graphTransformerMulti) Transform(g *Graph) error { | |||
40 | if err := t.Transform(g); err != nil { | 42 | if err := t.Transform(g); err != nil { |
41 | return err | 43 | return err |
42 | } | 44 | } |
45 | log.Printf( | ||
46 | "[TRACE] Graph after step %T:\n\n%s", | ||
47 | t, g.StringWithNodeTypes()) | ||
43 | } | 48 | } |
44 | 49 | ||
45 | return nil | 50 | return nil |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_attach_config_provider.go b/vendor/github.com/hashicorp/terraform/terraform/transform_attach_config_provider.go index 10506ea..39cf097 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_attach_config_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_attach_config_provider.go | |||
@@ -1,10 +1,7 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "log" | ||
5 | |||
6 | "github.com/hashicorp/terraform/config" | 4 | "github.com/hashicorp/terraform/config" |
7 | "github.com/hashicorp/terraform/config/module" | ||
8 | ) | 5 | ) |
9 | 6 | ||
10 | // GraphNodeAttachProvider is an interface that must be implemented by nodes | 7 | // GraphNodeAttachProvider is an interface that must be implemented by nodes |
@@ -19,62 +16,3 @@ type GraphNodeAttachProvider interface { | |||
19 | // Sets the configuration | 16 | // Sets the configuration |
20 | AttachProvider(*config.ProviderConfig) | 17 | AttachProvider(*config.ProviderConfig) |
21 | } | 18 | } |
22 | |||
23 | // AttachProviderConfigTransformer goes through the graph and attaches | ||
24 | // provider configuration structures to nodes that implement the interfaces | ||
25 | // above. | ||
26 | // | ||
27 | // The attached configuration structures are directly from the configuration. | ||
28 | // If they're going to be modified, a copy should be made. | ||
29 | type AttachProviderConfigTransformer struct { | ||
30 | Module *module.Tree // Module is the root module for the config | ||
31 | } | ||
32 | |||
33 | func (t *AttachProviderConfigTransformer) Transform(g *Graph) error { | ||
34 | if err := t.attachProviders(g); err != nil { | ||
35 | return err | ||
36 | } | ||
37 | |||
38 | return nil | ||
39 | } | ||
40 | |||
41 | func (t *AttachProviderConfigTransformer) attachProviders(g *Graph) error { | ||
42 | // Go through and find GraphNodeAttachProvider | ||
43 | for _, v := range g.Vertices() { | ||
44 | // Only care about GraphNodeAttachProvider implementations | ||
45 | apn, ok := v.(GraphNodeAttachProvider) | ||
46 | if !ok { | ||
47 | continue | ||
48 | } | ||
49 | |||
50 | // Determine what we're looking for | ||
51 | path := normalizeModulePath(apn.Path()) | ||
52 | path = path[1:] | ||
53 | name := apn.ProviderName() | ||
54 | log.Printf("[TRACE] Attach provider request: %#v %s", path, name) | ||
55 | |||
56 | // Get the configuration. | ||
57 | tree := t.Module.Child(path) | ||
58 | if tree == nil { | ||
59 | continue | ||
60 | } | ||
61 | |||
62 | // Go through the provider configs to find the matching config | ||
63 | for _, p := range tree.Config().ProviderConfigs { | ||
64 | // Build the name, which is "name.alias" if an alias exists | ||
65 | current := p.Name | ||
66 | if p.Alias != "" { | ||
67 | current += "." + p.Alias | ||
68 | } | ||
69 | |||
70 | // If the configs match then attach! | ||
71 | if current == name { | ||
72 | log.Printf("[TRACE] Attaching provider config: %#v", p) | ||
73 | apn.AttachProvider(p) | ||
74 | break | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | return nil | ||
80 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_deposed.go b/vendor/github.com/hashicorp/terraform/terraform/transform_deposed.go index 2148cef..87a1f9c 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_deposed.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_deposed.go | |||
@@ -12,6 +12,9 @@ type DeposedTransformer struct { | |||
12 | // View, if non-empty, is the ModuleState.View used around the state | 12 | // View, if non-empty, is the ModuleState.View used around the state |
13 | // to find deposed resources. | 13 | // to find deposed resources. |
14 | View string | 14 | View string |
15 | |||
16 | // The provider used by the resourced which were deposed | ||
17 | ResolvedProvider string | ||
15 | } | 18 | } |
16 | 19 | ||
17 | func (t *DeposedTransformer) Transform(g *Graph) error { | 20 | func (t *DeposedTransformer) Transform(g *Graph) error { |
@@ -33,14 +36,16 @@ func (t *DeposedTransformer) Transform(g *Graph) error { | |||
33 | if len(rs.Deposed) == 0 { | 36 | if len(rs.Deposed) == 0 { |
34 | continue | 37 | continue |
35 | } | 38 | } |
39 | |||
36 | deposed := rs.Deposed | 40 | deposed := rs.Deposed |
37 | 41 | ||
38 | for i, _ := range deposed { | 42 | for i, _ := range deposed { |
39 | g.Add(&graphNodeDeposedResource{ | 43 | g.Add(&graphNodeDeposedResource{ |
40 | Index: i, | 44 | Index: i, |
41 | ResourceName: k, | 45 | ResourceName: k, |
42 | ResourceType: rs.Type, | 46 | ResourceType: rs.Type, |
43 | Provider: rs.Provider, | 47 | ProviderName: rs.Provider, |
48 | ResolvedProvider: t.ResolvedProvider, | ||
44 | }) | 49 | }) |
45 | } | 50 | } |
46 | } | 51 | } |
@@ -50,18 +55,23 @@ func (t *DeposedTransformer) Transform(g *Graph) error { | |||
50 | 55 | ||
51 | // graphNodeDeposedResource is the graph vertex representing a deposed resource. | 56 | // graphNodeDeposedResource is the graph vertex representing a deposed resource. |
52 | type graphNodeDeposedResource struct { | 57 | type graphNodeDeposedResource struct { |
53 | Index int | 58 | Index int |
54 | ResourceName string | 59 | ResourceName string |
55 | ResourceType string | 60 | ResourceType string |
56 | Provider string | 61 | ProviderName string |
62 | ResolvedProvider string | ||
57 | } | 63 | } |
58 | 64 | ||
59 | func (n *graphNodeDeposedResource) Name() string { | 65 | func (n *graphNodeDeposedResource) Name() string { |
60 | return fmt.Sprintf("%s (deposed #%d)", n.ResourceName, n.Index) | 66 | return fmt.Sprintf("%s (deposed #%d)", n.ResourceName, n.Index) |
61 | } | 67 | } |
62 | 68 | ||
63 | func (n *graphNodeDeposedResource) ProvidedBy() []string { | 69 | func (n *graphNodeDeposedResource) ProvidedBy() string { |
64 | return []string{resourceProvider(n.ResourceName, n.Provider)} | 70 | return resourceProvider(n.ResourceName, n.ProviderName) |
71 | } | ||
72 | |||
73 | func (n *graphNodeDeposedResource) SetProvider(p string) { | ||
74 | n.ResolvedProvider = p | ||
65 | } | 75 | } |
66 | 76 | ||
67 | // GraphNodeEvalable impl. | 77 | // GraphNodeEvalable impl. |
@@ -81,7 +91,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode { | |||
81 | Node: &EvalSequence{ | 91 | Node: &EvalSequence{ |
82 | Nodes: []EvalNode{ | 92 | Nodes: []EvalNode{ |
83 | &EvalGetProvider{ | 93 | &EvalGetProvider{ |
84 | Name: n.ProvidedBy()[0], | 94 | Name: n.ResolvedProvider, |
85 | Output: &provider, | 95 | Output: &provider, |
86 | }, | 96 | }, |
87 | &EvalReadStateDeposed{ | 97 | &EvalReadStateDeposed{ |
@@ -98,7 +108,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode { | |||
98 | &EvalWriteStateDeposed{ | 108 | &EvalWriteStateDeposed{ |
99 | Name: n.ResourceName, | 109 | Name: n.ResourceName, |
100 | ResourceType: n.ResourceType, | 110 | ResourceType: n.ResourceType, |
101 | Provider: n.Provider, | 111 | Provider: n.ResolvedProvider, |
102 | State: &state, | 112 | State: &state, |
103 | Index: n.Index, | 113 | Index: n.Index, |
104 | }, | 114 | }, |
@@ -114,7 +124,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode { | |||
114 | Node: &EvalSequence{ | 124 | Node: &EvalSequence{ |
115 | Nodes: []EvalNode{ | 125 | Nodes: []EvalNode{ |
116 | &EvalGetProvider{ | 126 | &EvalGetProvider{ |
117 | Name: n.ProvidedBy()[0], | 127 | Name: n.ResolvedProvider, |
118 | Output: &provider, | 128 | Output: &provider, |
119 | }, | 129 | }, |
120 | &EvalReadStateDeposed{ | 130 | &EvalReadStateDeposed{ |
@@ -147,7 +157,7 @@ func (n *graphNodeDeposedResource) EvalTree() EvalNode { | |||
147 | &EvalWriteStateDeposed{ | 157 | &EvalWriteStateDeposed{ |
148 | Name: n.ResourceName, | 158 | Name: n.ResourceName, |
149 | ResourceType: n.ResourceType, | 159 | ResourceType: n.ResourceType, |
150 | Provider: n.Provider, | 160 | Provider: n.ResolvedProvider, |
151 | State: &state, | 161 | State: &state, |
152 | Index: n.Index, | 162 | Index: n.Index, |
153 | }, | 163 | }, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go index 22be1ab..a06ff29 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_destroy_edge.go | |||
@@ -119,17 +119,15 @@ func (t *DestroyEdgeTransformer) Transform(g *Graph) error { | |||
119 | return &NodeApplyableProvider{NodeAbstractProvider: a} | 119 | return &NodeApplyableProvider{NodeAbstractProvider: a} |
120 | } | 120 | } |
121 | steps := []GraphTransformer{ | 121 | steps := []GraphTransformer{ |
122 | // Add the local values | ||
123 | &LocalTransformer{Module: t.Module}, | ||
124 | |||
122 | // Add outputs and metadata | 125 | // Add outputs and metadata |
123 | &OutputTransformer{Module: t.Module}, | 126 | &OutputTransformer{Module: t.Module}, |
124 | &AttachResourceConfigTransformer{Module: t.Module}, | 127 | &AttachResourceConfigTransformer{Module: t.Module}, |
125 | &AttachStateTransformer{State: t.State}, | 128 | &AttachStateTransformer{State: t.State}, |
126 | 129 | ||
127 | // Add providers since they can affect destroy order as well | 130 | TransformProviders(nil, providerFn, t.Module), |
128 | &MissingProviderTransformer{AllowAny: true, Concrete: providerFn}, | ||
129 | &ProviderTransformer{}, | ||
130 | &DisableProviderTransformer{}, | ||
131 | &ParentProviderTransformer{}, | ||
132 | &AttachProviderConfigTransformer{Module: t.Module}, | ||
133 | 131 | ||
134 | // Add all the variables. We can depend on resources through | 132 | // Add all the variables. We can depend on resources through |
135 | // variables due to module parameters, and we need to properly | 133 | // variables due to module parameters, and we need to properly |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go b/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go index 081df2f..fcbff65 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_import_state.go | |||
@@ -21,9 +21,9 @@ func (t *ImportStateTransformer) Transform(g *Graph) error { | |||
21 | } | 21 | } |
22 | 22 | ||
23 | nodes = append(nodes, &graphNodeImportState{ | 23 | nodes = append(nodes, &graphNodeImportState{ |
24 | Addr: addr, | 24 | Addr: addr, |
25 | ID: target.ID, | 25 | ID: target.ID, |
26 | Provider: target.Provider, | 26 | ProviderName: target.Provider, |
27 | }) | 27 | }) |
28 | } | 28 | } |
29 | 29 | ||
@@ -36,9 +36,10 @@ func (t *ImportStateTransformer) Transform(g *Graph) error { | |||
36 | } | 36 | } |
37 | 37 | ||
38 | type graphNodeImportState struct { | 38 | type graphNodeImportState struct { |
39 | Addr *ResourceAddress // Addr is the resource address to import to | 39 | Addr *ResourceAddress // Addr is the resource address to import to |
40 | ID string // ID is the ID to import as | 40 | ID string // ID is the ID to import as |
41 | Provider string // Provider string | 41 | ProviderName string // Provider string |
42 | ResolvedProvider string // provider node address | ||
42 | 43 | ||
43 | states []*InstanceState | 44 | states []*InstanceState |
44 | } | 45 | } |
@@ -47,8 +48,12 @@ func (n *graphNodeImportState) Name() string { | |||
47 | return fmt.Sprintf("%s (import id: %s)", n.Addr, n.ID) | 48 | return fmt.Sprintf("%s (import id: %s)", n.Addr, n.ID) |
48 | } | 49 | } |
49 | 50 | ||
50 | func (n *graphNodeImportState) ProvidedBy() []string { | 51 | func (n *graphNodeImportState) ProvidedBy() string { |
51 | return []string{resourceProvider(n.Addr.Type, n.Provider)} | 52 | return resourceProvider(n.Addr.Type, n.ProviderName) |
53 | } | ||
54 | |||
55 | func (n *graphNodeImportState) SetProvider(p string) { | ||
56 | n.ResolvedProvider = p | ||
52 | } | 57 | } |
53 | 58 | ||
54 | // GraphNodeSubPath | 59 | // GraphNodeSubPath |
@@ -72,7 +77,7 @@ func (n *graphNodeImportState) EvalTree() EvalNode { | |||
72 | return &EvalSequence{ | 77 | return &EvalSequence{ |
73 | Nodes: []EvalNode{ | 78 | Nodes: []EvalNode{ |
74 | &EvalGetProvider{ | 79 | &EvalGetProvider{ |
75 | Name: n.ProvidedBy()[0], | 80 | Name: n.ResolvedProvider, |
76 | Output: &provider, | 81 | Output: &provider, |
77 | }, | 82 | }, |
78 | &EvalImportState{ | 83 | &EvalImportState{ |
@@ -149,10 +154,11 @@ func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) { | |||
149 | // is safe. | 154 | // is safe. |
150 | for i, state := range n.states { | 155 | for i, state := range n.states { |
151 | g.Add(&graphNodeImportStateSub{ | 156 | g.Add(&graphNodeImportStateSub{ |
152 | Target: addrs[i], | 157 | Target: addrs[i], |
153 | Path_: n.Path(), | 158 | Path_: n.Path(), |
154 | State: state, | 159 | State: state, |
155 | Provider: n.Provider, | 160 | ProviderName: n.ProviderName, |
161 | ResolvedProvider: n.ResolvedProvider, | ||
156 | }) | 162 | }) |
157 | } | 163 | } |
158 | 164 | ||
@@ -170,10 +176,11 @@ func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) { | |||
170 | // and is part of the subgraph. This node is responsible for refreshing | 176 | // and is part of the subgraph. This node is responsible for refreshing |
171 | // and adding a resource to the state once it is imported. | 177 | // and adding a resource to the state once it is imported. |
172 | type graphNodeImportStateSub struct { | 178 | type graphNodeImportStateSub struct { |
173 | Target *ResourceAddress | 179 | Target *ResourceAddress |
174 | State *InstanceState | 180 | State *InstanceState |
175 | Path_ []string | 181 | Path_ []string |
176 | Provider string | 182 | ProviderName string |
183 | ResolvedProvider string | ||
177 | } | 184 | } |
178 | 185 | ||
179 | func (n *graphNodeImportStateSub) Name() string { | 186 | func (n *graphNodeImportStateSub) Name() string { |
@@ -216,7 +223,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode { | |||
216 | return &EvalSequence{ | 223 | return &EvalSequence{ |
217 | Nodes: []EvalNode{ | 224 | Nodes: []EvalNode{ |
218 | &EvalGetProvider{ | 225 | &EvalGetProvider{ |
219 | Name: resourceProvider(info.Type, n.Provider), | 226 | Name: n.ResolvedProvider, |
220 | Output: &provider, | 227 | Output: &provider, |
221 | }, | 228 | }, |
222 | &EvalRefresh{ | 229 | &EvalRefresh{ |
@@ -233,7 +240,7 @@ func (n *graphNodeImportStateSub) EvalTree() EvalNode { | |||
233 | &EvalWriteState{ | 240 | &EvalWriteState{ |
234 | Name: key.String(), | 241 | Name: key.String(), |
235 | ResourceType: info.Type, | 242 | ResourceType: info.Type, |
236 | Provider: resourceProvider(info.Type, n.Provider), | 243 | Provider: n.ResolvedProvider, |
237 | State: &state, | 244 | State: &state, |
238 | }, | 245 | }, |
239 | }, | 246 | }, |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_local.go b/vendor/github.com/hashicorp/terraform/terraform/transform_local.go new file mode 100644 index 0000000..95ecfc0 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_local.go | |||
@@ -0,0 +1,40 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/terraform/config/module" | ||
5 | ) | ||
6 | |||
7 | // LocalTransformer is a GraphTransformer that adds all the local values | ||
8 | // from the configuration to the graph. | ||
9 | type LocalTransformer struct { | ||
10 | Module *module.Tree | ||
11 | } | ||
12 | |||
13 | func (t *LocalTransformer) Transform(g *Graph) error { | ||
14 | return t.transformModule(g, t.Module) | ||
15 | } | ||
16 | |||
17 | func (t *LocalTransformer) transformModule(g *Graph, m *module.Tree) error { | ||
18 | if m == nil { | ||
19 | // Can't have any locals if there's no config | ||
20 | return nil | ||
21 | } | ||
22 | |||
23 | for _, local := range m.Config().Locals { | ||
24 | node := &NodeLocal{ | ||
25 | PathValue: normalizeModulePath(m.Path()), | ||
26 | Config: local, | ||
27 | } | ||
28 | |||
29 | g.Add(node) | ||
30 | } | ||
31 | |||
32 | // Also populate locals for child modules | ||
33 | for _, c := range m.Children() { | ||
34 | if err := t.transformModule(g, c); err != nil { | ||
35 | return err | ||
36 | } | ||
37 | } | ||
38 | |||
39 | return nil | ||
40 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_output.go b/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_output.go index 49568d5..aea2bd0 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_output.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_orphan_output.go | |||
@@ -21,43 +21,32 @@ func (t *OrphanOutputTransformer) Transform(g *Graph) error { | |||
21 | return nil | 21 | return nil |
22 | } | 22 | } |
23 | 23 | ||
24 | return t.transform(g, t.Module) | 24 | for _, ms := range t.State.Modules { |
25 | } | 25 | if err := t.transform(g, ms); err != nil { |
26 | 26 | return err | |
27 | func (t *OrphanOutputTransformer) transform(g *Graph, m *module.Tree) error { | ||
28 | // Get our configuration, and recurse into children | ||
29 | var c *config.Config | ||
30 | if m != nil { | ||
31 | c = m.Config() | ||
32 | for _, child := range m.Children() { | ||
33 | if err := t.transform(g, child); err != nil { | ||
34 | return err | ||
35 | } | ||
36 | } | 27 | } |
37 | } | 28 | } |
29 | return nil | ||
30 | } | ||
38 | 31 | ||
39 | // Get the state. If there is no state, then we have no orphans! | 32 | func (t *OrphanOutputTransformer) transform(g *Graph, ms *ModuleState) error { |
40 | path := normalizeModulePath(m.Path()) | 33 | if ms == nil { |
41 | state := t.State.ModuleByPath(path) | ||
42 | if state == nil { | ||
43 | return nil | 34 | return nil |
44 | } | 35 | } |
45 | 36 | ||
46 | // Make a map of the valid outputs | 37 | path := normalizeModulePath(ms.Path) |
47 | valid := make(map[string]struct{}) | ||
48 | for _, o := range c.Outputs { | ||
49 | valid[o.Name] = struct{}{} | ||
50 | } | ||
51 | 38 | ||
52 | // Go through the outputs and find the ones that aren't in our config. | 39 | // Get the config for this path, which is nil if the entire module has been |
53 | for n, _ := range state.Outputs { | 40 | // removed. |
54 | // If it is in the valid map, then ignore | 41 | var c *config.Config |
55 | if _, ok := valid[n]; ok { | 42 | if m := t.Module.Child(path[1:]); m != nil { |
56 | continue | 43 | c = m.Config() |
57 | } | 44 | } |
58 | 45 | ||
59 | // Orphan! | 46 | // add all the orphaned outputs to the graph |
47 | for _, n := range ms.RemovedOutputs(c) { | ||
60 | g.Add(&NodeOutputOrphan{OutputName: n, PathValue: path}) | 48 | g.Add(&NodeOutputOrphan{OutputName: n, PathValue: path}) |
49 | |||
61 | } | 50 | } |
62 | 51 | ||
63 | return nil | 52 | return nil |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_output.go b/vendor/github.com/hashicorp/terraform/terraform/transform_output.go index b260f4c..faa25e4 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_output.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_output.go | |||
@@ -1,7 +1,10 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "log" | ||
5 | |||
4 | "github.com/hashicorp/terraform/config/module" | 6 | "github.com/hashicorp/terraform/config/module" |
7 | "github.com/hashicorp/terraform/dag" | ||
5 | ) | 8 | ) |
6 | 9 | ||
7 | // OutputTransformer is a GraphTransformer that adds all the outputs | 10 | // OutputTransformer is a GraphTransformer that adds all the outputs |
@@ -41,11 +44,6 @@ func (t *OutputTransformer) transform(g *Graph, m *module.Tree) error { | |||
41 | 44 | ||
42 | // Add all outputs here | 45 | // Add all outputs here |
43 | for _, o := range os { | 46 | for _, o := range os { |
44 | // Build the node. | ||
45 | // | ||
46 | // NOTE: For now this is just an "applyable" output. As we build | ||
47 | // new graph builders for the other operations I suspect we'll | ||
48 | // find a way to parameterize this, require new transforms, etc. | ||
49 | node := &NodeApplyableOutput{ | 47 | node := &NodeApplyableOutput{ |
50 | PathValue: normalizeModulePath(m.Path()), | 48 | PathValue: normalizeModulePath(m.Path()), |
51 | Config: o, | 49 | Config: o, |
@@ -57,3 +55,41 @@ func (t *OutputTransformer) transform(g *Graph, m *module.Tree) error { | |||
57 | 55 | ||
58 | return nil | 56 | return nil |
59 | } | 57 | } |
58 | |||
59 | // DestroyOutputTransformer is a GraphTransformer that adds nodes to delete | ||
60 | // outputs during destroy. We need to do this to ensure that no stale outputs | ||
61 | // are ever left in the state. | ||
62 | type DestroyOutputTransformer struct { | ||
63 | } | ||
64 | |||
65 | func (t *DestroyOutputTransformer) Transform(g *Graph) error { | ||
66 | for _, v := range g.Vertices() { | ||
67 | output, ok := v.(*NodeApplyableOutput) | ||
68 | if !ok { | ||
69 | continue | ||
70 | } | ||
71 | |||
72 | // create the destroy node for this output | ||
73 | node := &NodeDestroyableOutput{ | ||
74 | PathValue: output.PathValue, | ||
75 | Config: output.Config, | ||
76 | } | ||
77 | |||
78 | log.Printf("[TRACE] creating %s", node.Name()) | ||
79 | g.Add(node) | ||
80 | |||
81 | deps, err := g.Descendents(v) | ||
82 | if err != nil { | ||
83 | return err | ||
84 | } | ||
85 | |||
86 | // the destroy node must depend on the eval node | ||
87 | deps.Add(v) | ||
88 | |||
89 | for _, d := range deps.List() { | ||
90 | log.Printf("[TRACE] %s depends on %s", node.Name(), dag.VertexName(d)) | ||
91 | g.Connect(dag.BasicEdge(node, d)) | ||
92 | } | ||
93 | } | ||
94 | return nil | ||
95 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go b/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go index b9695d5..c4772b4 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_provider.go | |||
@@ -1,19 +1,46 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "errors" | ||
4 | "fmt" | 5 | "fmt" |
5 | "log" | 6 | "log" |
6 | "strings" | 7 | "strings" |
7 | 8 | ||
8 | "github.com/hashicorp/go-multierror" | 9 | "github.com/hashicorp/go-multierror" |
10 | "github.com/hashicorp/terraform/config" | ||
11 | "github.com/hashicorp/terraform/config/module" | ||
9 | "github.com/hashicorp/terraform/dag" | 12 | "github.com/hashicorp/terraform/dag" |
10 | ) | 13 | ) |
11 | 14 | ||
15 | func TransformProviders(providers []string, concrete ConcreteProviderNodeFunc, mod *module.Tree) GraphTransformer { | ||
16 | return GraphTransformMulti( | ||
17 | // Add providers from the config | ||
18 | &ProviderConfigTransformer{ | ||
19 | Module: mod, | ||
20 | Providers: providers, | ||
21 | Concrete: concrete, | ||
22 | }, | ||
23 | // Add any remaining missing providers | ||
24 | &MissingProviderTransformer{ | ||
25 | Providers: providers, | ||
26 | Concrete: concrete, | ||
27 | }, | ||
28 | // Connect the providers | ||
29 | &ProviderTransformer{}, | ||
30 | // Remove unused providers and proxies | ||
31 | &PruneProviderTransformer{}, | ||
32 | // Connect provider to their parent provider nodes | ||
33 | &ParentProviderTransformer{}, | ||
34 | ) | ||
35 | } | ||
36 | |||
12 | // GraphNodeProvider is an interface that nodes that can be a provider | 37 | // GraphNodeProvider is an interface that nodes that can be a provider |
13 | // must implement. The ProviderName returned is the name of the provider | 38 | // must implement. |
14 | // they satisfy. | 39 | // ProviderName returns the name of the provider this satisfies. |
40 | // Name returns the full name of the provider in the config. | ||
15 | type GraphNodeProvider interface { | 41 | type GraphNodeProvider interface { |
16 | ProviderName() string | 42 | ProviderName() string |
43 | Name() string | ||
17 | } | 44 | } |
18 | 45 | ||
19 | // GraphNodeCloseProvider is an interface that nodes that can be a close | 46 | // GraphNodeCloseProvider is an interface that nodes that can be a close |
@@ -25,9 +52,12 @@ type GraphNodeCloseProvider interface { | |||
25 | 52 | ||
26 | // GraphNodeProviderConsumer is an interface that nodes that require | 53 | // GraphNodeProviderConsumer is an interface that nodes that require |
27 | // a provider must implement. ProvidedBy must return the name of the provider | 54 | // a provider must implement. ProvidedBy must return the name of the provider |
28 | // to use. | 55 | // to use. This may be a provider by type, type.alias or a fully resolved |
56 | // provider name | ||
29 | type GraphNodeProviderConsumer interface { | 57 | type GraphNodeProviderConsumer interface { |
30 | ProvidedBy() []string | 58 | ProvidedBy() string |
59 | // Set the resolved provider address for this resource. | ||
60 | SetProvider(string) | ||
31 | } | 61 | } |
32 | 62 | ||
33 | // ProviderTransformer is a GraphTransformer that maps resources to | 63 | // ProviderTransformer is a GraphTransformer that maps resources to |
@@ -41,18 +71,52 @@ func (t *ProviderTransformer) Transform(g *Graph) error { | |||
41 | m := providerVertexMap(g) | 71 | m := providerVertexMap(g) |
42 | for _, v := range g.Vertices() { | 72 | for _, v := range g.Vertices() { |
43 | if pv, ok := v.(GraphNodeProviderConsumer); ok { | 73 | if pv, ok := v.(GraphNodeProviderConsumer); ok { |
44 | for _, p := range pv.ProvidedBy() { | 74 | p := pv.ProvidedBy() |
45 | target := m[providerMapKey(p, pv)] | 75 | |
46 | if target == nil { | 76 | key := providerMapKey(p, pv) |
47 | println(fmt.Sprintf("%#v\n\n%#v", m, providerMapKey(p, pv))) | 77 | target := m[key] |
48 | err = multierror.Append(err, fmt.Errorf( | 78 | |
49 | "%s: provider %s couldn't be found", | 79 | sp, ok := pv.(GraphNodeSubPath) |
50 | dag.VertexName(v), p)) | 80 | if !ok && target == nil { |
51 | continue | 81 | // no target, and no path to walk up |
82 | err = multierror.Append(err, fmt.Errorf( | ||
83 | "%s: provider %s couldn't be found", | ||
84 | dag.VertexName(v), p)) | ||
85 | break | ||
86 | } | ||
87 | |||
88 | // if we don't have a provider at this level, walk up the path looking for one | ||
89 | for i := 1; target == nil; i++ { | ||
90 | path := normalizeModulePath(sp.Path()) | ||
91 | if len(path) < i { | ||
92 | break | ||
93 | } | ||
94 | |||
95 | key = ResolveProviderName(p, path[:len(path)-i]) | ||
96 | target = m[key] | ||
97 | if target != nil { | ||
98 | break | ||
52 | } | 99 | } |
100 | } | ||
101 | |||
102 | if target == nil { | ||
103 | err = multierror.Append(err, fmt.Errorf( | ||
104 | "%s: configuration for %s is not present; a provider configuration block is required for all operations", | ||
105 | dag.VertexName(v), p, | ||
106 | )) | ||
107 | break | ||
108 | } | ||
53 | 109 | ||
54 | g.Connect(dag.BasicEdge(v, target)) | 110 | // see if this in an inherited provider |
111 | if p, ok := target.(*graphNodeProxyProvider); ok { | ||
112 | g.Remove(p) | ||
113 | target = p.Target() | ||
114 | key = target.(GraphNodeProvider).Name() | ||
55 | } | 115 | } |
116 | |||
117 | log.Printf("[DEBUG] resource %s using provider %s", dag.VertexName(pv), key) | ||
118 | pv.SetProvider(key) | ||
119 | g.Connect(dag.BasicEdge(v, target)) | ||
56 | } | 120 | } |
57 | } | 121 | } |
58 | 122 | ||
@@ -67,36 +131,32 @@ type CloseProviderTransformer struct{} | |||
67 | 131 | ||
68 | func (t *CloseProviderTransformer) Transform(g *Graph) error { | 132 | func (t *CloseProviderTransformer) Transform(g *Graph) error { |
69 | pm := providerVertexMap(g) | 133 | pm := providerVertexMap(g) |
70 | cpm := closeProviderVertexMap(g) | 134 | cpm := make(map[string]*graphNodeCloseProvider) |
71 | var err error | 135 | var err error |
72 | for _, v := range g.Vertices() { | ||
73 | if pv, ok := v.(GraphNodeProviderConsumer); ok { | ||
74 | for _, p := range pv.ProvidedBy() { | ||
75 | key := p | ||
76 | source := cpm[key] | ||
77 | |||
78 | if source == nil { | ||
79 | // Create a new graphNodeCloseProvider and add it to the graph | ||
80 | source = &graphNodeCloseProvider{ProviderNameValue: p} | ||
81 | g.Add(source) | ||
82 | |||
83 | // Close node needs to depend on provider | ||
84 | provider, ok := pm[key] | ||
85 | if !ok { | ||
86 | err = multierror.Append(err, fmt.Errorf( | ||
87 | "%s: provider %s couldn't be found for closing", | ||
88 | dag.VertexName(v), p)) | ||
89 | continue | ||
90 | } | ||
91 | g.Connect(dag.BasicEdge(source, provider)) | ||
92 | |||
93 | // Make sure we also add the new graphNodeCloseProvider to the map | ||
94 | // so we don't create and add any duplicate graphNodeCloseProviders. | ||
95 | cpm[key] = source | ||
96 | } | ||
97 | 136 | ||
98 | // Close node depends on all nodes provided by the provider | 137 | for _, v := range pm { |
99 | g.Connect(dag.BasicEdge(source, v)) | 138 | p := v.(GraphNodeProvider) |
139 | |||
140 | // get the close provider of this type if we alread created it | ||
141 | closer := cpm[p.Name()] | ||
142 | |||
143 | if closer == nil { | ||
144 | // create a closer for this provider type | ||
145 | closer = &graphNodeCloseProvider{ProviderNameValue: p.Name()} | ||
146 | g.Add(closer) | ||
147 | cpm[p.Name()] = closer | ||
148 | } | ||
149 | |||
150 | // Close node depends on the provider itself | ||
151 | // this is added unconditionally, so it will connect to all instances | ||
152 | // of the provider. Extra edges will be removed by transitive | ||
153 | // reduction. | ||
154 | g.Connect(dag.BasicEdge(closer, p)) | ||
155 | |||
156 | // connect all the provider's resources to the close node | ||
157 | for _, s := range g.UpEdges(p).List() { | ||
158 | if _, ok := s.(GraphNodeProviderConsumer); ok { | ||
159 | g.Connect(dag.BasicEdge(closer, s)) | ||
100 | } | 160 | } |
101 | } | 161 | } |
102 | } | 162 | } |
@@ -104,18 +164,14 @@ func (t *CloseProviderTransformer) Transform(g *Graph) error { | |||
104 | return err | 164 | return err |
105 | } | 165 | } |
106 | 166 | ||
107 | // MissingProviderTransformer is a GraphTransformer that adds nodes | 167 | // MissingProviderTransformer is a GraphTransformer that adds nodes for all |
108 | // for missing providers into the graph. Specifically, it creates provider | 168 | // required providers into the graph. Specifically, it creates provider |
109 | // configuration nodes for all the providers that we support. These are | 169 | // configuration nodes for all the providers that we support. These are pruned |
110 | // pruned later during an optimization pass. | 170 | // later during an optimization pass. |
111 | type MissingProviderTransformer struct { | 171 | type MissingProviderTransformer struct { |
112 | // Providers is the list of providers we support. | 172 | // Providers is the list of providers we support. |
113 | Providers []string | 173 | Providers []string |
114 | 174 | ||
115 | // AllowAny will not check that a provider is supported before adding | ||
116 | // it to the graph. | ||
117 | AllowAny bool | ||
118 | |||
119 | // Concrete, if set, overrides how the providers are made. | 175 | // Concrete, if set, overrides how the providers are made. |
120 | Concrete ConcreteProviderNodeFunc | 176 | Concrete ConcreteProviderNodeFunc |
121 | } | 177 | } |
@@ -128,99 +184,57 @@ func (t *MissingProviderTransformer) Transform(g *Graph) error { | |||
128 | } | 184 | } |
129 | } | 185 | } |
130 | 186 | ||
131 | // Create a set of our supported providers | 187 | var err error |
132 | supported := make(map[string]struct{}, len(t.Providers)) | ||
133 | for _, v := range t.Providers { | ||
134 | supported[v] = struct{}{} | ||
135 | } | ||
136 | |||
137 | // Get the map of providers we already have in our graph | ||
138 | m := providerVertexMap(g) | 188 | m := providerVertexMap(g) |
139 | 189 | for _, v := range g.Vertices() { | |
140 | // Go through all the provider consumers and make sure we add | ||
141 | // that provider if it is missing. We use a for loop here instead | ||
142 | // of "range" since we'll modify check as we go to add more to check. | ||
143 | check := g.Vertices() | ||
144 | for i := 0; i < len(check); i++ { | ||
145 | v := check[i] | ||
146 | |||
147 | pv, ok := v.(GraphNodeProviderConsumer) | 190 | pv, ok := v.(GraphNodeProviderConsumer) |
148 | if !ok { | 191 | if !ok { |
149 | continue | 192 | continue |
150 | } | 193 | } |
151 | 194 | ||
152 | // If this node has a subpath, then we use that as a prefix | 195 | p := pv.ProvidedBy() |
153 | // into our map to check for an existing provider. | 196 | // this may be the resolved provider from the state, so we need to get |
154 | var path []string | 197 | // the base provider name. |
155 | if sp, ok := pv.(GraphNodeSubPath); ok { | 198 | parts := strings.SplitAfter(p, "provider.") |
156 | raw := normalizeModulePath(sp.Path()) | 199 | p = parts[len(parts)-1] |
157 | if len(raw) > len(rootModulePath) { | ||
158 | path = raw | ||
159 | } | ||
160 | } | ||
161 | 200 | ||
162 | for _, p := range pv.ProvidedBy() { | 201 | key := ResolveProviderName(p, nil) |
163 | key := providerMapKey(p, pv) | 202 | provider := m[key] |
164 | if _, ok := m[key]; ok { | ||
165 | // This provider already exists as a configure node | ||
166 | continue | ||
167 | } | ||
168 | 203 | ||
169 | // If the provider has an alias in it, we just want the type | 204 | // we already have it |
170 | ptype := p | 205 | if provider != nil { |
171 | if idx := strings.IndexRune(p, '.'); idx != -1 { | 206 | continue |
172 | ptype = p[:idx] | 207 | } |
173 | } | ||
174 | 208 | ||
175 | if !t.AllowAny { | 209 | // we don't implicitly create aliased providers |
176 | if _, ok := supported[ptype]; !ok { | 210 | if strings.Contains(p, ".") { |
177 | // If we don't support the provider type, skip it. | 211 | log.Println("[DEBUG] not adding missing provider alias:", p) |
178 | // Validation later will catch this as an error. | 212 | continue |
179 | continue | 213 | } |
180 | } | ||
181 | } | ||
182 | 214 | ||
183 | // Add the missing provider node to the graph | 215 | log.Println("[DEBUG] adding missing provider:", p) |
184 | v := t.Concrete(&NodeAbstractProvider{ | ||
185 | NameValue: p, | ||
186 | PathValue: path, | ||
187 | }).(dag.Vertex) | ||
188 | if len(path) > 0 { | ||
189 | // We'll need the parent provider as well, so let's | ||
190 | // add a dummy node to check to make sure that we add | ||
191 | // that parent provider. | ||
192 | check = append(check, &graphNodeProviderConsumerDummy{ | ||
193 | ProviderValue: p, | ||
194 | PathValue: path[:len(path)-1], | ||
195 | }) | ||
196 | } | ||
197 | 216 | ||
198 | m[key] = g.Add(v) | 217 | // create the misisng top-level provider |
199 | } | 218 | provider = t.Concrete(&NodeAbstractProvider{ |
219 | NameValue: p, | ||
220 | }).(dag.Vertex) | ||
221 | |||
222 | m[key] = g.Add(provider) | ||
200 | } | 223 | } |
201 | 224 | ||
202 | return nil | 225 | return err |
203 | } | 226 | } |
204 | 227 | ||
205 | // ParentProviderTransformer connects provider nodes to their parents. | 228 | // ParentProviderTransformer connects provider nodes to their parents. |
206 | // | 229 | // |
207 | // This works by finding nodes that are both GraphNodeProviders and | 230 | // This works by finding nodes that are both GraphNodeProviders and |
208 | // GraphNodeSubPath. It then connects the providers to their parent | 231 | // GraphNodeSubPath. It then connects the providers to their parent |
209 | // path. | 232 | // path. The parent provider is always at the root level. |
210 | type ParentProviderTransformer struct{} | 233 | type ParentProviderTransformer struct{} |
211 | 234 | ||
212 | func (t *ParentProviderTransformer) Transform(g *Graph) error { | 235 | func (t *ParentProviderTransformer) Transform(g *Graph) error { |
213 | // Make a mapping of path to dag.Vertex, where path is: "path.name" | 236 | pm := providerVertexMap(g) |
214 | m := make(map[string]dag.Vertex) | 237 | for _, v := range g.Vertices() { |
215 | |||
216 | // Also create a map that maps a provider to its parent | ||
217 | parentMap := make(map[dag.Vertex]string) | ||
218 | for _, raw := range g.Vertices() { | ||
219 | // If it is the flat version, then make it the non-flat version. | ||
220 | // We eventually want to get rid of the flat version entirely so | ||
221 | // this is a stop-gap while it still exists. | ||
222 | var v dag.Vertex = raw | ||
223 | |||
224 | // Only care about providers | 238 | // Only care about providers |
225 | pn, ok := v.(GraphNodeProvider) | 239 | pn, ok := v.(GraphNodeProvider) |
226 | if !ok || pn.ProviderName() == "" { | 240 | if !ok || pn.ProviderName() == "" { |
@@ -228,53 +242,48 @@ func (t *ParentProviderTransformer) Transform(g *Graph) error { | |||
228 | } | 242 | } |
229 | 243 | ||
230 | // Also require a subpath, if there is no subpath then we | 244 | // Also require a subpath, if there is no subpath then we |
231 | // just totally ignore it. The expectation of this transform is | 245 | // can't have a parent. |
232 | // that it is used with a graph builder that is already flattened. | 246 | if pn, ok := v.(GraphNodeSubPath); ok { |
233 | var path []string | 247 | if len(normalizeModulePath(pn.Path())) <= 1 { |
234 | if pn, ok := raw.(GraphNodeSubPath); ok { | 248 | continue |
235 | path = pn.Path() | 249 | } |
236 | } | ||
237 | path = normalizeModulePath(path) | ||
238 | |||
239 | // Build the key with path.name i.e. "child.subchild.aws" | ||
240 | key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName()) | ||
241 | m[key] = raw | ||
242 | |||
243 | // Determine the parent if we're non-root. This is length 1 since | ||
244 | // the 0 index should be "root" since we normalize above. | ||
245 | if len(path) > 1 { | ||
246 | path = path[:len(path)-1] | ||
247 | key := fmt.Sprintf("%s.%s", strings.Join(path, "."), pn.ProviderName()) | ||
248 | parentMap[raw] = key | ||
249 | } | 250 | } |
250 | } | ||
251 | 251 | ||
252 | // Connect! | 252 | // this provider may be disabled, but we can only get it's name from |
253 | for v, key := range parentMap { | 253 | // the ProviderName string |
254 | if parent, ok := m[key]; ok { | 254 | name := ResolveProviderName(strings.SplitN(pn.ProviderName(), " ", 2)[0], nil) |
255 | parent := pm[name] | ||
256 | if parent != nil { | ||
255 | g.Connect(dag.BasicEdge(v, parent)) | 257 | g.Connect(dag.BasicEdge(v, parent)) |
256 | } | 258 | } |
257 | } | ||
258 | 259 | ||
260 | } | ||
259 | return nil | 261 | return nil |
260 | } | 262 | } |
261 | 263 | ||
262 | // PruneProviderTransformer is a GraphTransformer that prunes all the | 264 | // PruneProviderTransformer removes any providers that are not actually used by |
263 | // providers that aren't needed from the graph. A provider is unneeded if | 265 | // anything, and provider proxies. This avoids the provider being initialized |
264 | // no resource or module is using that provider. | 266 | // and configured. This both saves resources but also avoids errors since |
267 | // configuration may imply initialization which may require auth. | ||
265 | type PruneProviderTransformer struct{} | 268 | type PruneProviderTransformer struct{} |
266 | 269 | ||
267 | func (t *PruneProviderTransformer) Transform(g *Graph) error { | 270 | func (t *PruneProviderTransformer) Transform(g *Graph) error { |
268 | for _, v := range g.Vertices() { | 271 | for _, v := range g.Vertices() { |
269 | // We only care about the providers | 272 | // We only care about providers |
270 | if pn, ok := v.(GraphNodeProvider); !ok || pn.ProviderName() == "" { | 273 | pn, ok := v.(GraphNodeProvider) |
274 | if !ok || pn.ProviderName() == "" { | ||
271 | continue | 275 | continue |
272 | } | 276 | } |
273 | // Does anything depend on this? If not, then prune it. | 277 | |
274 | if s := g.UpEdges(v); s.Len() == 0 { | 278 | // ProxyProviders will have up edges, but we're now done with them in the graph |
275 | if nv, ok := v.(dag.NamedVertex); ok { | 279 | if _, ok := v.(*graphNodeProxyProvider); ok { |
276 | log.Printf("[DEBUG] Pruning provider with no dependencies: %s", nv.Name()) | 280 | log.Printf("[DEBUG] pruning proxy provider %s", dag.VertexName(v)) |
277 | } | 281 | g.Remove(v) |
282 | } | ||
283 | |||
284 | // Remove providers with no dependencies. | ||
285 | if g.UpEdges(v).Len() == 0 { | ||
286 | log.Printf("[DEBUG] pruning unused provider %s", dag.VertexName(v)) | ||
278 | g.Remove(v) | 287 | g.Remove(v) |
279 | } | 288 | } |
280 | } | 289 | } |
@@ -285,23 +294,26 @@ func (t *PruneProviderTransformer) Transform(g *Graph) error { | |||
285 | // providerMapKey is a helper that gives us the key to use for the | 294 | // providerMapKey is a helper that gives us the key to use for the |
286 | // maps returned by things such as providerVertexMap. | 295 | // maps returned by things such as providerVertexMap. |
287 | func providerMapKey(k string, v dag.Vertex) string { | 296 | func providerMapKey(k string, v dag.Vertex) string { |
288 | pathPrefix := "" | 297 | if strings.Contains(k, "provider.") { |
289 | if sp, ok := v.(GraphNodeSubPath); ok { | 298 | // this is already resolved |
290 | raw := normalizeModulePath(sp.Path()) | 299 | return k |
291 | if len(raw) > len(rootModulePath) { | ||
292 | pathPrefix = modulePrefixStr(raw) + "." | ||
293 | } | ||
294 | } | 300 | } |
295 | 301 | ||
296 | return pathPrefix + k | 302 | // we create a dummy provider to |
303 | var path []string | ||
304 | if sp, ok := v.(GraphNodeSubPath); ok { | ||
305 | path = normalizeModulePath(sp.Path()) | ||
306 | } | ||
307 | return ResolveProviderName(k, path) | ||
297 | } | 308 | } |
298 | 309 | ||
299 | func providerVertexMap(g *Graph) map[string]dag.Vertex { | 310 | func providerVertexMap(g *Graph) map[string]dag.Vertex { |
300 | m := make(map[string]dag.Vertex) | 311 | m := make(map[string]dag.Vertex) |
301 | for _, v := range g.Vertices() { | 312 | for _, v := range g.Vertices() { |
302 | if pv, ok := v.(GraphNodeProvider); ok { | 313 | if pv, ok := v.(GraphNodeProvider); ok { |
303 | key := providerMapKey(pv.ProviderName(), v) | 314 | // TODO: The Name may have meta info, like " (disabled)" |
304 | m[key] = v | 315 | name := strings.SplitN(pv.Name(), " ", 2)[0] |
316 | m[name] = v | ||
305 | } | 317 | } |
306 | } | 318 | } |
307 | 319 | ||
@@ -324,7 +336,7 @@ type graphNodeCloseProvider struct { | |||
324 | } | 336 | } |
325 | 337 | ||
326 | func (n *graphNodeCloseProvider) Name() string { | 338 | func (n *graphNodeCloseProvider) Name() string { |
327 | return fmt.Sprintf("provider.%s (close)", n.ProviderNameValue) | 339 | return n.ProviderNameValue + " (close)" |
328 | } | 340 | } |
329 | 341 | ||
330 | // GraphNodeEvalable impl. | 342 | // GraphNodeEvalable impl. |
@@ -362,19 +374,233 @@ func (n *graphNodeCloseProvider) RemoveIfNotTargeted() bool { | |||
362 | return true | 374 | return true |
363 | } | 375 | } |
364 | 376 | ||
365 | // graphNodeProviderConsumerDummy is a struct that never enters the real | 377 | // graphNodeProxyProvider is a GraphNodeProvider implementation that is used to |
366 | // graph (though it could to no ill effect). It implements | 378 | // store the name and value of a provider node for inheritance between modules. |
367 | // GraphNodeProviderConsumer and GraphNodeSubpath as a way to force | 379 | // These nodes are only used to store the data while loading the provider |
368 | // certain transformations. | 380 | // configurations, and are removed after all the resources have been connected |
369 | type graphNodeProviderConsumerDummy struct { | 381 | // to their providers. |
370 | ProviderValue string | 382 | type graphNodeProxyProvider struct { |
371 | PathValue []string | 383 | nameValue string |
384 | path []string | ||
385 | target GraphNodeProvider | ||
386 | } | ||
387 | |||
388 | func (n *graphNodeProxyProvider) ProviderName() string { | ||
389 | return n.Target().ProviderName() | ||
390 | } | ||
391 | |||
392 | func (n *graphNodeProxyProvider) Name() string { | ||
393 | return ResolveProviderName(n.nameValue, n.path) | ||
394 | } | ||
395 | |||
396 | // find the concrete provider instance | ||
397 | func (n *graphNodeProxyProvider) Target() GraphNodeProvider { | ||
398 | switch t := n.target.(type) { | ||
399 | case *graphNodeProxyProvider: | ||
400 | return t.Target() | ||
401 | default: | ||
402 | return n.target | ||
403 | } | ||
404 | } | ||
405 | |||
406 | // ProviderConfigTransformer adds all provider nodes from the configuration and | ||
407 | // attaches the configs. | ||
408 | type ProviderConfigTransformer struct { | ||
409 | Providers []string | ||
410 | Concrete ConcreteProviderNodeFunc | ||
411 | |||
412 | // each provider node is stored here so that the proxy nodes can look up | ||
413 | // their targets by name. | ||
414 | providers map[string]GraphNodeProvider | ||
415 | // record providers that can be overriden with a proxy | ||
416 | proxiable map[string]bool | ||
417 | |||
418 | // Module is the module to add resources from. | ||
419 | Module *module.Tree | ||
372 | } | 420 | } |
373 | 421 | ||
374 | func (n *graphNodeProviderConsumerDummy) Path() []string { | 422 | func (t *ProviderConfigTransformer) Transform(g *Graph) error { |
375 | return n.PathValue | 423 | // If no module is given, we don't do anything |
424 | if t.Module == nil { | ||
425 | return nil | ||
426 | } | ||
427 | |||
428 | // If the module isn't loaded, that is simply an error | ||
429 | if !t.Module.Loaded() { | ||
430 | return errors.New("module must be loaded for ProviderConfigTransformer") | ||
431 | } | ||
432 | |||
433 | t.providers = make(map[string]GraphNodeProvider) | ||
434 | t.proxiable = make(map[string]bool) | ||
435 | |||
436 | // Start the transformation process | ||
437 | if err := t.transform(g, t.Module); err != nil { | ||
438 | return err | ||
439 | } | ||
440 | |||
441 | // finally attach the configs to the new nodes | ||
442 | return t.attachProviderConfigs(g) | ||
376 | } | 443 | } |
377 | 444 | ||
378 | func (n *graphNodeProviderConsumerDummy) ProvidedBy() []string { | 445 | func (t *ProviderConfigTransformer) transform(g *Graph, m *module.Tree) error { |
379 | return []string{n.ProviderValue} | 446 | // If no config, do nothing |
447 | if m == nil { | ||
448 | return nil | ||
449 | } | ||
450 | |||
451 | // Add our resources | ||
452 | if err := t.transformSingle(g, m); err != nil { | ||
453 | return err | ||
454 | } | ||
455 | |||
456 | // Transform all the children. | ||
457 | for _, c := range m.Children() { | ||
458 | if err := t.transform(g, c); err != nil { | ||
459 | return err | ||
460 | } | ||
461 | } | ||
462 | return nil | ||
463 | } | ||
464 | |||
465 | func (t *ProviderConfigTransformer) transformSingle(g *Graph, m *module.Tree) error { | ||
466 | log.Printf("[TRACE] ProviderConfigTransformer: Starting for path: %v", m.Path()) | ||
467 | |||
468 | // Get the configuration for this module | ||
469 | conf := m.Config() | ||
470 | |||
471 | // Build the path we're at | ||
472 | path := m.Path() | ||
473 | if len(path) > 0 { | ||
474 | path = append([]string{RootModuleName}, path...) | ||
475 | } | ||
476 | |||
477 | // add all providers from the configuration | ||
478 | for _, p := range conf.ProviderConfigs { | ||
479 | name := p.Name | ||
480 | if p.Alias != "" { | ||
481 | name += "." + p.Alias | ||
482 | } | ||
483 | |||
484 | v := t.Concrete(&NodeAbstractProvider{ | ||
485 | NameValue: name, | ||
486 | PathValue: path, | ||
487 | }) | ||
488 | |||
489 | // Add it to the graph | ||
490 | g.Add(v) | ||
491 | fullName := ResolveProviderName(name, path) | ||
492 | t.providers[fullName] = v.(GraphNodeProvider) | ||
493 | t.proxiable[fullName] = len(p.RawConfig.RawMap()) == 0 | ||
494 | } | ||
495 | |||
496 | // Now replace the provider nodes with proxy nodes if a provider was being | ||
497 | // passed in, and create implicit proxies if there was no config. Any extra | ||
498 | // proxies will be removed in the prune step. | ||
499 | return t.addProxyProviders(g, m) | ||
500 | } | ||
501 | |||
502 | func (t *ProviderConfigTransformer) addProxyProviders(g *Graph, m *module.Tree) error { | ||
503 | path := m.Path() | ||
504 | |||
505 | // can't add proxies at the root | ||
506 | if len(path) == 0 { | ||
507 | return nil | ||
508 | } | ||
509 | |||
510 | parentPath := path[:len(path)-1] | ||
511 | parent := t.Module.Child(parentPath) | ||
512 | if parent == nil { | ||
513 | return nil | ||
514 | } | ||
515 | |||
516 | var parentCfg *config.Module | ||
517 | for _, mod := range parent.Config().Modules { | ||
518 | if mod.Name == m.Name() { | ||
519 | parentCfg = mod | ||
520 | break | ||
521 | } | ||
522 | } | ||
523 | |||
524 | if parentCfg == nil { | ||
525 | // this can't really happen during normal execution. | ||
526 | return fmt.Errorf("parent module config not found for %s", m.Name()) | ||
527 | } | ||
528 | |||
529 | // Go through all the providers the parent is passing in, and add proxies to | ||
530 | // the parent provider nodes. | ||
531 | for name, parentName := range parentCfg.Providers { | ||
532 | fullName := ResolveProviderName(name, path) | ||
533 | fullParentName := ResolveProviderName(parentName, parentPath) | ||
534 | |||
535 | parentProvider := t.providers[fullParentName] | ||
536 | |||
537 | if parentProvider == nil { | ||
538 | return fmt.Errorf("missing provider %s", fullParentName) | ||
539 | } | ||
540 | |||
541 | proxy := &graphNodeProxyProvider{ | ||
542 | nameValue: name, | ||
543 | path: path, | ||
544 | target: parentProvider, | ||
545 | } | ||
546 | |||
547 | concreteProvider := t.providers[fullName] | ||
548 | |||
549 | // replace the concrete node with the provider passed in | ||
550 | if concreteProvider != nil && t.proxiable[fullName] { | ||
551 | g.Replace(concreteProvider, proxy) | ||
552 | t.providers[fullName] = proxy | ||
553 | continue | ||
554 | } | ||
555 | |||
556 | // aliased providers can't be implicitly passed in | ||
557 | if strings.Contains(name, ".") { | ||
558 | continue | ||
559 | } | ||
560 | |||
561 | // There was no concrete provider, so add this as an implicit provider. | ||
562 | // The extra proxy will be pruned later if it's unused. | ||
563 | g.Add(proxy) | ||
564 | t.providers[fullName] = proxy | ||
565 | } | ||
566 | return nil | ||
567 | } | ||
568 | |||
569 | func (t *ProviderConfigTransformer) attachProviderConfigs(g *Graph) error { | ||
570 | for _, v := range g.Vertices() { | ||
571 | // Only care about GraphNodeAttachProvider implementations | ||
572 | apn, ok := v.(GraphNodeAttachProvider) | ||
573 | if !ok { | ||
574 | continue | ||
575 | } | ||
576 | |||
577 | // Determine what we're looking for | ||
578 | path := normalizeModulePath(apn.Path())[1:] | ||
579 | name := apn.ProviderName() | ||
580 | log.Printf("[TRACE] Attach provider request: %#v %s", path, name) | ||
581 | |||
582 | // Get the configuration. | ||
583 | tree := t.Module.Child(path) | ||
584 | if tree == nil { | ||
585 | continue | ||
586 | } | ||
587 | |||
588 | // Go through the provider configs to find the matching config | ||
589 | for _, p := range tree.Config().ProviderConfigs { | ||
590 | // Build the name, which is "name.alias" if an alias exists | ||
591 | current := p.Name | ||
592 | if p.Alias != "" { | ||
593 | current += "." + p.Alias | ||
594 | } | ||
595 | |||
596 | // If the configs match then attach! | ||
597 | if current == name { | ||
598 | log.Printf("[TRACE] Attaching provider config: %#v", p) | ||
599 | apn.AttachProvider(p) | ||
600 | break | ||
601 | } | ||
602 | } | ||
603 | } | ||
604 | |||
605 | return nil | ||
380 | } | 606 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_provider_disable.go b/vendor/github.com/hashicorp/terraform/terraform/transform_provider_disable.go deleted file mode 100644 index d9919f3..0000000 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_provider_disable.go +++ /dev/null | |||
@@ -1,50 +0,0 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "github.com/hashicorp/terraform/dag" | ||
7 | ) | ||
8 | |||
9 | // DisableProviderTransformer "disables" any providers that are not actually | ||
10 | // used by anything. This avoids the provider being initialized and configured. | ||
11 | // This both saves resources but also avoids errors since configuration | ||
12 | // may imply initialization which may require auth. | ||
13 | type DisableProviderTransformer struct{} | ||
14 | |||
15 | func (t *DisableProviderTransformer) Transform(g *Graph) error { | ||
16 | for _, v := range g.Vertices() { | ||
17 | // We only care about providers | ||
18 | pn, ok := v.(GraphNodeProvider) | ||
19 | if !ok || pn.ProviderName() == "" { | ||
20 | continue | ||
21 | } | ||
22 | |||
23 | // If we have dependencies, then don't disable | ||
24 | if g.UpEdges(v).Len() > 0 { | ||
25 | continue | ||
26 | } | ||
27 | |||
28 | // Get the path | ||
29 | var path []string | ||
30 | if pn, ok := v.(GraphNodeSubPath); ok { | ||
31 | path = pn.Path() | ||
32 | } | ||
33 | |||
34 | // Disable the provider by replacing it with a "disabled" provider | ||
35 | disabled := &NodeDisabledProvider{ | ||
36 | NodeAbstractProvider: &NodeAbstractProvider{ | ||
37 | NameValue: pn.ProviderName(), | ||
38 | PathValue: path, | ||
39 | }, | ||
40 | } | ||
41 | |||
42 | if !g.Replace(v, disabled) { | ||
43 | panic(fmt.Sprintf( | ||
44 | "vertex disappeared from under us: %s", | ||
45 | dag.VertexName(v))) | ||
46 | } | ||
47 | } | ||
48 | |||
49 | return nil | ||
50 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go b/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go index c545235..be8c7f9 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_reference.go | |||
@@ -76,6 +76,85 @@ func (t *ReferenceTransformer) Transform(g *Graph) error { | |||
76 | return nil | 76 | return nil |
77 | } | 77 | } |
78 | 78 | ||
79 | // DestroyReferenceTransformer is a GraphTransformer that reverses the edges | ||
80 | // for locals and outputs that depend on other nodes which will be | ||
81 | // removed during destroy. If a destroy node is evaluated before the local or | ||
82 | // output value, it will be removed from the state, and the later interpolation | ||
83 | // will fail. | ||
84 | type DestroyValueReferenceTransformer struct{} | ||
85 | |||
86 | func (t *DestroyValueReferenceTransformer) Transform(g *Graph) error { | ||
87 | vs := g.Vertices() | ||
88 | for _, v := range vs { | ||
89 | switch v.(type) { | ||
90 | case *NodeApplyableOutput, *NodeLocal: | ||
91 | // OK | ||
92 | default: | ||
93 | continue | ||
94 | } | ||
95 | |||
96 | // reverse any outgoing edges so that the value is evaluated first. | ||
97 | for _, e := range g.EdgesFrom(v) { | ||
98 | target := e.Target() | ||
99 | |||
100 | // only destroy nodes will be evaluated in reverse | ||
101 | if _, ok := target.(GraphNodeDestroyer); !ok { | ||
102 | continue | ||
103 | } | ||
104 | |||
105 | log.Printf("[TRACE] output dep: %s", dag.VertexName(target)) | ||
106 | |||
107 | g.RemoveEdge(e) | ||
108 | g.Connect(&DestroyEdge{S: target, T: v}) | ||
109 | } | ||
110 | } | ||
111 | |||
112 | return nil | ||
113 | } | ||
114 | |||
115 | // PruneUnusedValuesTransformer is s GraphTransformer that removes local and | ||
116 | // output values which are not referenced in the graph. Since outputs and | ||
117 | // locals always need to be evaluated, if they reference a resource that is not | ||
118 | // available in the state the interpolation could fail. | ||
119 | type PruneUnusedValuesTransformer struct{} | ||
120 | |||
121 | func (t *PruneUnusedValuesTransformer) Transform(g *Graph) error { | ||
122 | // this might need multiple runs in order to ensure that pruning a value | ||
123 | // doesn't effect a previously checked value. | ||
124 | for removed := 0; ; removed = 0 { | ||
125 | for _, v := range g.Vertices() { | ||
126 | switch v.(type) { | ||
127 | case *NodeApplyableOutput, *NodeLocal: | ||
128 | // OK | ||
129 | default: | ||
130 | continue | ||
131 | } | ||
132 | |||
133 | dependants := g.UpEdges(v) | ||
134 | |||
135 | switch dependants.Len() { | ||
136 | case 0: | ||
137 | // nothing at all depends on this | ||
138 | g.Remove(v) | ||
139 | removed++ | ||
140 | case 1: | ||
141 | // because an output's destroy node always depends on the output, | ||
142 | // we need to check for the case of a single destroy node. | ||
143 | d := dependants.List()[0] | ||
144 | if _, ok := d.(*NodeDestroyableOutput); ok { | ||
145 | g.Remove(v) | ||
146 | removed++ | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | if removed == 0 { | ||
151 | break | ||
152 | } | ||
153 | } | ||
154 | |||
155 | return nil | ||
156 | } | ||
157 | |||
79 | // ReferenceMap is a structure that can be used to efficiently check | 158 | // ReferenceMap is a structure that can be used to efficiently check |
80 | // for references on a graph. | 159 | // for references on a graph. |
81 | type ReferenceMap struct { | 160 | type ReferenceMap struct { |
@@ -96,6 +175,7 @@ func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) { | |||
96 | var matches []dag.Vertex | 175 | var matches []dag.Vertex |
97 | var missing []string | 176 | var missing []string |
98 | prefix := m.prefix(v) | 177 | prefix := m.prefix(v) |
178 | |||
99 | for _, ns := range rn.References() { | 179 | for _, ns := range rn.References() { |
100 | found := false | 180 | found := false |
101 | for _, n := range strings.Split(ns, "/") { | 181 | for _, n := range strings.Split(ns, "/") { |
@@ -108,19 +188,14 @@ func (m *ReferenceMap) References(v dag.Vertex) ([]dag.Vertex, []string) { | |||
108 | // Mark that we found a match | 188 | // Mark that we found a match |
109 | found = true | 189 | found = true |
110 | 190 | ||
111 | // Make sure this isn't a self reference, which isn't included | ||
112 | selfRef := false | ||
113 | for _, p := range parents { | 191 | for _, p := range parents { |
192 | // don't include self-references | ||
114 | if p == v { | 193 | if p == v { |
115 | selfRef = true | 194 | continue |
116 | break | ||
117 | } | 195 | } |
118 | } | 196 | matches = append(matches, p) |
119 | if selfRef { | ||
120 | continue | ||
121 | } | 197 | } |
122 | 198 | ||
123 | matches = append(matches, parents...) | ||
124 | break | 199 | break |
125 | } | 200 | } |
126 | 201 | ||
@@ -296,14 +371,21 @@ func ReferenceFromInterpolatedVar(v config.InterpolatedVariable) []string { | |||
296 | return []string{fmt.Sprintf("%s.%d/%s.N", id, idx, id)} | 371 | return []string{fmt.Sprintf("%s.%d/%s.N", id, idx, id)} |
297 | case *config.UserVariable: | 372 | case *config.UserVariable: |
298 | return []string{fmt.Sprintf("var.%s", v.Name)} | 373 | return []string{fmt.Sprintf("var.%s", v.Name)} |
374 | case *config.LocalVariable: | ||
375 | return []string{fmt.Sprintf("local.%s", v.Name)} | ||
299 | default: | 376 | default: |
300 | return nil | 377 | return nil |
301 | } | 378 | } |
302 | } | 379 | } |
303 | 380 | ||
304 | func modulePrefixStr(p []string) string { | 381 | func modulePrefixStr(p []string) string { |
382 | // strip "root" | ||
383 | if len(p) > 0 && p[0] == rootModulePath[0] { | ||
384 | p = p[1:] | ||
385 | } | ||
386 | |||
305 | parts := make([]string, 0, len(p)*2) | 387 | parts := make([]string, 0, len(p)*2) |
306 | for _, p := range p[1:] { | 388 | for _, p := range p { |
307 | parts = append(parts, "module", p) | 389 | parts = append(parts, "module", p) |
308 | } | 390 | } |
309 | 391 | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_removed_modules.go b/vendor/github.com/hashicorp/terraform/terraform/transform_removed_modules.go new file mode 100644 index 0000000..2e05edb --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_removed_modules.go | |||
@@ -0,0 +1,32 @@ | |||
1 | package terraform | ||
2 | |||
3 | import ( | ||
4 | "log" | ||
5 | |||
6 | "github.com/hashicorp/terraform/config/module" | ||
7 | ) | ||
8 | |||
9 | // RemoveModuleTransformer implements GraphTransformer to add nodes indicating | ||
10 | // when a module was removed from the configuration. | ||
11 | type RemovedModuleTransformer struct { | ||
12 | Module *module.Tree // root module | ||
13 | State *State | ||
14 | } | ||
15 | |||
16 | func (t *RemovedModuleTransformer) Transform(g *Graph) error { | ||
17 | // nothing to remove if there's no state! | ||
18 | if t.State == nil { | ||
19 | return nil | ||
20 | } | ||
21 | |||
22 | for _, m := range t.State.Modules { | ||
23 | c := t.Module.Child(m.Path[1:]) | ||
24 | if c != nil { | ||
25 | continue | ||
26 | } | ||
27 | |||
28 | log.Printf("[DEBUG] module %s no longer in config\n", modulePrefixStr(m.Path)) | ||
29 | g.Add(&NodeModuleRemoved{PathValue: m.Path}) | ||
30 | } | ||
31 | return nil | ||
32 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_resource_count.go b/vendor/github.com/hashicorp/terraform/terraform/transform_resource_count.go index cda35cb..e528b37 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_resource_count.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_resource_count.go | |||
@@ -37,7 +37,9 @@ func (t *ResourceCountTransformer) Transform(g *Graph) error { | |||
37 | addr.Index = index | 37 | addr.Index = index |
38 | 38 | ||
39 | // Build the abstract node and the concrete one | 39 | // Build the abstract node and the concrete one |
40 | abstract := &NodeAbstractResource{Addr: addr} | 40 | abstract := &NodeAbstractResource{ |
41 | Addr: addr, | ||
42 | } | ||
41 | var node dag.Vertex = abstract | 43 | var node dag.Vertex = abstract |
42 | if f := t.Concrete; f != nil { | 44 | if f := t.Concrete; f != nil { |
43 | node = f(abstract) | 45 | node = f(abstract) |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go b/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go index 4f117b4..af6defe 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go +++ b/vendor/github.com/hashicorp/terraform/terraform/transform_targets.go | |||
@@ -73,9 +73,11 @@ func (t *TargetsTransformer) Transform(g *Graph) error { | |||
73 | if _, ok := v.(GraphNodeResource); ok { | 73 | if _, ok := v.(GraphNodeResource); ok { |
74 | removable = true | 74 | removable = true |
75 | } | 75 | } |
76 | |||
76 | if vr, ok := v.(RemovableIfNotTargeted); ok { | 77 | if vr, ok := v.(RemovableIfNotTargeted); ok { |
77 | removable = vr.RemoveIfNotTargeted() | 78 | removable = vr.RemoveIfNotTargeted() |
78 | } | 79 | } |
80 | |||
79 | if removable && !targetedNodes.Include(v) { | 81 | if removable && !targetedNodes.Include(v) { |
80 | log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v)) | 82 | log.Printf("[DEBUG] Removing %q, filtered by targeting.", dag.VertexName(v)) |
81 | g.Remove(v) | 83 | g.Remove(v) |
@@ -135,7 +137,10 @@ func (t *TargetsTransformer) selectTargetedNodes( | |||
135 | } | 137 | } |
136 | } | 138 | } |
137 | } | 139 | } |
140 | return t.addDependencies(targetedNodes, g) | ||
141 | } | ||
138 | 142 | ||
143 | func (t *TargetsTransformer) addDependencies(targetedNodes *dag.Set, g *Graph) (*dag.Set, error) { | ||
139 | // Handle nodes that need to be included if their dependencies are included. | 144 | // Handle nodes that need to be included if their dependencies are included. |
140 | // This requires multiple passes since we need to catch transitive | 145 | // This requires multiple passes since we need to catch transitive |
141 | // dependencies if and only if they are via other nodes that also | 146 | // dependencies if and only if they are via other nodes that also |
@@ -157,11 +162,6 @@ func (t *TargetsTransformer) selectTargetedNodes( | |||
157 | } | 162 | } |
158 | 163 | ||
159 | dependers = dependers.Filter(func(dv interface{}) bool { | 164 | dependers = dependers.Filter(func(dv interface{}) bool { |
160 | // Can ignore nodes that are already targeted | ||
161 | /*if targetedNodes.Include(dv) { | ||
162 | return false | ||
163 | }*/ | ||
164 | |||
165 | _, ok := dv.(GraphNodeTargetDownstream) | 165 | _, ok := dv.(GraphNodeTargetDownstream) |
166 | return ok | 166 | return ok |
167 | }) | 167 | }) |
@@ -180,6 +180,7 @@ func (t *TargetsTransformer) selectTargetedNodes( | |||
180 | // depending on in case that informs its decision about whether | 180 | // depending on in case that informs its decision about whether |
181 | // it is safe to be targeted. | 181 | // it is safe to be targeted. |
182 | deps := g.DownEdges(v) | 182 | deps := g.DownEdges(v) |
183 | |||
183 | depsTargeted := deps.Intersection(targetedNodes) | 184 | depsTargeted := deps.Intersection(targetedNodes) |
184 | depsUntargeted := deps.Difference(depsTargeted) | 185 | depsUntargeted := deps.Difference(depsTargeted) |
185 | 186 | ||
@@ -193,7 +194,50 @@ func (t *TargetsTransformer) selectTargetedNodes( | |||
193 | } | 194 | } |
194 | } | 195 | } |
195 | 196 | ||
196 | return targetedNodes, nil | 197 | return targetedNodes.Filter(func(dv interface{}) bool { |
198 | return filterPartialOutputs(dv, targetedNodes, g) | ||
199 | }), nil | ||
200 | } | ||
201 | |||
202 | // Outputs may have been included transitively, but if any of their | ||
203 | // dependencies have been pruned they won't be resolvable. | ||
204 | // If nothing depends on the output, and the output is missing any | ||
205 | // dependencies, remove it from the graph. | ||
206 | // This essentially maintains the previous behavior where interpolation in | ||
207 | // outputs would fail silently, but can now surface errors where the output | ||
208 | // is required. | ||
209 | func filterPartialOutputs(v interface{}, targetedNodes *dag.Set, g *Graph) bool { | ||
210 | // should this just be done with TargetDownstream? | ||
211 | if _, ok := v.(*NodeApplyableOutput); !ok { | ||
212 | return true | ||
213 | } | ||
214 | |||
215 | dependers := g.UpEdges(v) | ||
216 | for _, d := range dependers.List() { | ||
217 | if _, ok := d.(*NodeCountBoundary); ok { | ||
218 | continue | ||
219 | } | ||
220 | |||
221 | if !targetedNodes.Include(d) { | ||
222 | // this one is going to be removed, so it doesn't count | ||
223 | continue | ||
224 | } | ||
225 | |||
226 | // as soon as we see a real dependency, we mark this as | ||
227 | // non-removable | ||
228 | return true | ||
229 | } | ||
230 | |||
231 | depends := g.DownEdges(v) | ||
232 | |||
233 | for _, d := range depends.List() { | ||
234 | if !targetedNodes.Include(d) { | ||
235 | log.Printf("[WARN] %s missing targeted dependency %s, removing from the graph", | ||
236 | dag.VertexName(v), dag.VertexName(d)) | ||
237 | return false | ||
238 | } | ||
239 | } | ||
240 | return true | ||
197 | } | 241 | } |
198 | 242 | ||
199 | func (t *TargetsTransformer) nodeIsTarget( | 243 | func (t *TargetsTransformer) nodeIsTarget( |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/ui_output_mock.go b/vendor/github.com/hashicorp/terraform/terraform/ui_output_mock.go index 7852bc4..d828c92 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/ui_output_mock.go +++ b/vendor/github.com/hashicorp/terraform/terraform/ui_output_mock.go | |||
@@ -1,13 +1,18 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import "sync" | ||
4 | |||
3 | // MockUIOutput is an implementation of UIOutput that can be used for tests. | 5 | // MockUIOutput is an implementation of UIOutput that can be used for tests. |
4 | type MockUIOutput struct { | 6 | type MockUIOutput struct { |
7 | sync.Mutex | ||
5 | OutputCalled bool | 8 | OutputCalled bool |
6 | OutputMessage string | 9 | OutputMessage string |
7 | OutputFn func(string) | 10 | OutputFn func(string) |
8 | } | 11 | } |
9 | 12 | ||
10 | func (o *MockUIOutput) Output(v string) { | 13 | func (o *MockUIOutput) Output(v string) { |
14 | o.Lock() | ||
15 | defer o.Unlock() | ||
11 | o.OutputCalled = true | 16 | o.OutputCalled = true |
12 | o.OutputMessage = v | 17 | o.OutputMessage = v |
13 | if o.OutputFn != nil { | 18 | if o.OutputFn != nil { |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/user_agent.go b/vendor/github.com/hashicorp/terraform/terraform/user_agent.go index 700be2a..a42613e 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/user_agent.go +++ b/vendor/github.com/hashicorp/terraform/terraform/user_agent.go | |||
@@ -1,14 +1,13 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "github.com/hashicorp/terraform/httpclient" |
5 | "runtime" | ||
6 | ) | 5 | ) |
7 | 6 | ||
8 | // The standard Terraform User-Agent format | ||
9 | const UserAgent = "Terraform %s (%s)" | ||
10 | |||
11 | // Generate a UserAgent string | 7 | // Generate a UserAgent string |
8 | // | ||
9 | // Deprecated: Use httpclient.UserAgentString if you are setting your | ||
10 | // own User-Agent header. | ||
12 | func UserAgentString() string { | 11 | func UserAgentString() string { |
13 | return fmt.Sprintf(UserAgent, VersionString(), runtime.Version()) | 12 | return httpclient.UserAgentString() |
14 | } | 13 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/version.go b/vendor/github.com/hashicorp/terraform/terraform/version.go index d61b11e..ac73015 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/version.go +++ b/vendor/github.com/hashicorp/terraform/terraform/version.go | |||
@@ -1,31 +1,10 @@ | |||
1 | package terraform | 1 | package terraform |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "fmt" | 4 | "github.com/hashicorp/terraform/version" |
5 | |||
6 | "github.com/hashicorp/go-version" | ||
7 | ) | 5 | ) |
8 | 6 | ||
9 | // The main version number that is being run at the moment. | 7 | // TODO: update providers to use the version package directly |
10 | const Version = "0.10.0" | ||
11 | |||
12 | // A pre-release marker for the version. If this is "" (empty string) | ||
13 | // then it means that it is a final release. Otherwise, this is a pre-release | ||
14 | // such as "dev" (in development), "beta", "rc1", etc. | ||
15 | var VersionPrerelease = "dev" | ||
16 | |||
17 | // SemVersion is an instance of version.Version. This has the secondary | ||
18 | // benefit of verifying during tests and init time that our version is a | ||
19 | // proper semantic version, which should always be the case. | ||
20 | var SemVersion = version.Must(version.NewVersion(Version)) | ||
21 | |||
22 | // VersionHeader is the header name used to send the current terraform version | ||
23 | // in http requests. | ||
24 | const VersionHeader = "Terraform-Version" | ||
25 | |||
26 | func VersionString() string { | 8 | func VersionString() string { |
27 | if VersionPrerelease != "" { | 9 | return version.String() |
28 | return fmt.Sprintf("%s-%s", Version, VersionPrerelease) | ||
29 | } | ||
30 | return Version | ||
31 | } | 10 | } |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/version_required.go b/vendor/github.com/hashicorp/terraform/terraform/version_required.go index 3cbbf56..1f43045 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/version_required.go +++ b/vendor/github.com/hashicorp/terraform/terraform/version_required.go | |||
@@ -6,19 +6,21 @@ import ( | |||
6 | "github.com/hashicorp/go-version" | 6 | "github.com/hashicorp/go-version" |
7 | "github.com/hashicorp/terraform/config" | 7 | "github.com/hashicorp/terraform/config" |
8 | "github.com/hashicorp/terraform/config/module" | 8 | "github.com/hashicorp/terraform/config/module" |
9 | |||
10 | tfversion "github.com/hashicorp/terraform/version" | ||
9 | ) | 11 | ) |
10 | 12 | ||
11 | // checkRequiredVersion verifies that any version requirements specified by | 13 | // CheckRequiredVersion verifies that any version requirements specified by |
12 | // the configuration are met. | 14 | // the configuration are met. |
13 | // | 15 | // |
14 | // This checks the root module as well as any additional version requirements | 16 | // This checks the root module as well as any additional version requirements |
15 | // from child modules. | 17 | // from child modules. |
16 | // | 18 | // |
17 | // This is tested in context_test.go. | 19 | // This is tested in context_test.go. |
18 | func checkRequiredVersion(m *module.Tree) error { | 20 | func CheckRequiredVersion(m *module.Tree) error { |
19 | // Check any children | 21 | // Check any children |
20 | for _, c := range m.Children() { | 22 | for _, c := range m.Children() { |
21 | if err := checkRequiredVersion(c); err != nil { | 23 | if err := CheckRequiredVersion(c); err != nil { |
22 | return err | 24 | return err |
23 | } | 25 | } |
24 | } | 26 | } |
@@ -49,7 +51,7 @@ func checkRequiredVersion(m *module.Tree) error { | |||
49 | tf.RequiredVersion, err) | 51 | tf.RequiredVersion, err) |
50 | } | 52 | } |
51 | 53 | ||
52 | if !cs.Check(SemVersion) { | 54 | if !cs.Check(tfversion.SemVer) { |
53 | return fmt.Errorf( | 55 | return fmt.Errorf( |
54 | "The currently running version of Terraform doesn't meet the\n"+ | 56 | "The currently running version of Terraform doesn't meet the\n"+ |
55 | "version requirements explicitly specified by the configuration.\n"+ | 57 | "version requirements explicitly specified by the configuration.\n"+ |
@@ -62,7 +64,7 @@ func checkRequiredVersion(m *module.Tree) error { | |||
62 | " Current version: %s", | 64 | " Current version: %s", |
63 | module, | 65 | module, |
64 | tf.RequiredVersion, | 66 | tf.RequiredVersion, |
65 | SemVersion) | 67 | tfversion.SemVer) |
66 | } | 68 | } |
67 | 69 | ||
68 | return nil | 70 | return nil |
diff --git a/vendor/github.com/hashicorp/terraform/terraform/walkoperation_string.go b/vendor/github.com/hashicorp/terraform/terraform/walkoperation_string.go index cbd78dd..4cfc528 100644 --- a/vendor/github.com/hashicorp/terraform/terraform/walkoperation_string.go +++ b/vendor/github.com/hashicorp/terraform/terraform/walkoperation_string.go | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | package terraform | 3 | package terraform |
4 | 4 | ||
5 | import "fmt" | 5 | import "strconv" |
6 | 6 | ||
7 | const _walkOperation_name = "walkInvalidwalkInputwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidatewalkDestroywalkImport" | 7 | const _walkOperation_name = "walkInvalidwalkInputwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidatewalkDestroywalkImport" |
8 | 8 | ||
@@ -10,7 +10,7 @@ var _walkOperation_index = [...]uint8{0, 11, 20, 29, 37, 52, 63, 75, 86, 96} | |||
10 | 10 | ||
11 | func (i walkOperation) String() string { | 11 | func (i walkOperation) String() string { |
12 | if i >= walkOperation(len(_walkOperation_index)-1) { | 12 | if i >= walkOperation(len(_walkOperation_index)-1) { |
13 | return fmt.Sprintf("walkOperation(%d)", i) | 13 | return "walkOperation(" + strconv.FormatInt(int64(i), 10) + ")" |
14 | } | 14 | } |
15 | return _walkOperation_name[_walkOperation_index[i]:_walkOperation_index[i+1]] | 15 | return _walkOperation_name[_walkOperation_index[i]:_walkOperation_index[i+1]] |
16 | } | 16 | } |
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go new file mode 100644 index 0000000..2c23f76 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostic.go | |||
@@ -0,0 +1,26 @@ | |||
1 | package tfdiags | ||
2 | |||
3 | type Diagnostic interface { | ||
4 | Severity() Severity | ||
5 | Description() Description | ||
6 | Source() Source | ||
7 | } | ||
8 | |||
9 | type Severity rune | ||
10 | |||
11 | //go:generate stringer -type=Severity | ||
12 | |||
13 | const ( | ||
14 | Error Severity = 'E' | ||
15 | Warning Severity = 'W' | ||
16 | ) | ||
17 | |||
18 | type Description struct { | ||
19 | Summary string | ||
20 | Detail string | ||
21 | } | ||
22 | |||
23 | type Source struct { | ||
24 | Subject *SourceRange | ||
25 | Context *SourceRange | ||
26 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go new file mode 100644 index 0000000..667ba80 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/tfdiags/diagnostics.go | |||
@@ -0,0 +1,181 @@ | |||
1 | package tfdiags | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "fmt" | ||
6 | |||
7 | "github.com/hashicorp/errwrap" | ||
8 | multierror "github.com/hashicorp/go-multierror" | ||
9 | "github.com/hashicorp/hcl2/hcl" | ||
10 | ) | ||
11 | |||
12 | // Diagnostics is a list of diagnostics. Diagnostics is intended to be used | ||
13 | // where a Go "error" might normally be used, allowing richer information | ||
14 | // to be conveyed (more context, support for warnings). | ||
15 | // | ||
16 | // A nil Diagnostics is a valid, empty diagnostics list, thus allowing | ||
17 | // heap allocation to be avoided in the common case where there are no | ||
18 | // diagnostics to report at all. | ||
19 | type Diagnostics []Diagnostic | ||
20 | |||
21 | // Append is the main interface for constructing Diagnostics lists, taking | ||
22 | // an existing list (which may be nil) and appending the new objects to it | ||
23 | // after normalizing them to be implementations of Diagnostic. | ||
24 | // | ||
25 | // The usual pattern for a function that natively "speaks" diagnostics is: | ||
26 | // | ||
27 | // // Create a nil Diagnostics at the start of the function | ||
28 | // var diags diag.Diagnostics | ||
29 | // | ||
30 | // // At later points, build on it if errors / warnings occur: | ||
31 | // foo, err := DoSomethingRisky() | ||
32 | // if err != nil { | ||
33 | // diags = diags.Append(err) | ||
34 | // } | ||
35 | // | ||
36 | // // Eventually return the result and diagnostics in place of error | ||
37 | // return result, diags | ||
38 | // | ||
39 | // Append accepts a variety of different diagnostic-like types, including | ||
40 | // native Go errors and HCL diagnostics. It also knows how to unwrap | ||
41 | // a multierror.Error into separate error diagnostics. It can be passed | ||
42 | // another Diagnostics to concatenate the two lists. If given something | ||
43 | // it cannot handle, this function will panic. | ||
44 | func (diags Diagnostics) Append(new ...interface{}) Diagnostics { | ||
45 | for _, item := range new { | ||
46 | if item == nil { | ||
47 | continue | ||
48 | } | ||
49 | |||
50 | switch ti := item.(type) { | ||
51 | case Diagnostic: | ||
52 | diags = append(diags, ti) | ||
53 | case Diagnostics: | ||
54 | diags = append(diags, ti...) // flatten | ||
55 | case diagnosticsAsError: | ||
56 | diags = diags.Append(ti.Diagnostics) // unwrap | ||
57 | case hcl.Diagnostics: | ||
58 | for _, hclDiag := range ti { | ||
59 | diags = append(diags, hclDiagnostic{hclDiag}) | ||
60 | } | ||
61 | case *hcl.Diagnostic: | ||
62 | diags = append(diags, hclDiagnostic{ti}) | ||
63 | case *multierror.Error: | ||
64 | for _, err := range ti.Errors { | ||
65 | diags = append(diags, nativeError{err}) | ||
66 | } | ||
67 | case error: | ||
68 | switch { | ||
69 | case errwrap.ContainsType(ti, Diagnostics(nil)): | ||
70 | // If we have an errwrap wrapper with a Diagnostics hiding | ||
71 | // inside then we'll unpick it here to get access to the | ||
72 | // individual diagnostics. | ||
73 | diags = diags.Append(errwrap.GetType(ti, Diagnostics(nil))) | ||
74 | case errwrap.ContainsType(ti, hcl.Diagnostics(nil)): | ||
75 | // Likewise, if we have HCL diagnostics we'll unpick that too. | ||
76 | diags = diags.Append(errwrap.GetType(ti, hcl.Diagnostics(nil))) | ||
77 | default: | ||
78 | diags = append(diags, nativeError{ti}) | ||
79 | } | ||
80 | default: | ||
81 | panic(fmt.Errorf("can't construct diagnostic(s) from %T", item)) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | // Given the above, we should never end up with a non-nil empty slice | ||
86 | // here, but we'll make sure of that so callers can rely on empty == nil | ||
87 | if len(diags) == 0 { | ||
88 | return nil | ||
89 | } | ||
90 | |||
91 | return diags | ||
92 | } | ||
93 | |||
94 | // HasErrors returns true if any of the diagnostics in the list have | ||
95 | // a severity of Error. | ||
96 | func (diags Diagnostics) HasErrors() bool { | ||
97 | for _, diag := range diags { | ||
98 | if diag.Severity() == Error { | ||
99 | return true | ||
100 | } | ||
101 | } | ||
102 | return false | ||
103 | } | ||
104 | |||
105 | // ForRPC returns a version of the receiver that has been simplified so that | ||
106 | // it is friendly to RPC protocols. | ||
107 | // | ||
108 | // Currently this means that it can be serialized with encoding/gob and | ||
109 | // subsequently re-inflated. It may later grow to include other serialization | ||
110 | // formats. | ||
111 | // | ||
112 | // Note that this loses information about the original objects used to | ||
113 | // construct the diagnostics, so e.g. the errwrap API will not work as | ||
114 | // expected on an error-wrapped Diagnostics that came from ForRPC. | ||
115 | func (diags Diagnostics) ForRPC() Diagnostics { | ||
116 | ret := make(Diagnostics, len(diags)) | ||
117 | for i := range diags { | ||
118 | ret[i] = makeRPCFriendlyDiag(diags[i]) | ||
119 | } | ||
120 | return ret | ||
121 | } | ||
122 | |||
123 | // Err flattens a diagnostics list into a single Go error, or to nil | ||
124 | // if the diagnostics list does not include any error-level diagnostics. | ||
125 | // | ||
126 | // This can be used to smuggle diagnostics through an API that deals in | ||
127 | // native errors, but unfortunately it will lose naked warnings (warnings | ||
128 | // that aren't accompanied by at least one error) since such APIs have no | ||
129 | // mechanism through which to report these. | ||
130 | // | ||
131 | // return result, diags.Error() | ||
132 | func (diags Diagnostics) Err() error { | ||
133 | if !diags.HasErrors() { | ||
134 | return nil | ||
135 | } | ||
136 | return diagnosticsAsError{diags} | ||
137 | } | ||
138 | |||
139 | type diagnosticsAsError struct { | ||
140 | Diagnostics | ||
141 | } | ||
142 | |||
143 | func (dae diagnosticsAsError) Error() string { | ||
144 | diags := dae.Diagnostics | ||
145 | switch { | ||
146 | case len(diags) == 0: | ||
147 | // should never happen, since we don't create this wrapper if | ||
148 | // there are no diagnostics in the list. | ||
149 | return "no errors" | ||
150 | case len(diags) == 1: | ||
151 | desc := diags[0].Description() | ||
152 | if desc.Detail == "" { | ||
153 | return desc.Summary | ||
154 | } | ||
155 | return fmt.Sprintf("%s: %s", desc.Summary, desc.Detail) | ||
156 | default: | ||
157 | var ret bytes.Buffer | ||
158 | fmt.Fprintf(&ret, "%d problems:\n", len(diags)) | ||
159 | for _, diag := range dae.Diagnostics { | ||
160 | desc := diag.Description() | ||
161 | if desc.Detail == "" { | ||
162 | fmt.Fprintf(&ret, "\n- %s", desc.Summary) | ||
163 | } else { | ||
164 | fmt.Fprintf(&ret, "\n- %s: %s", desc.Summary, desc.Detail) | ||
165 | } | ||
166 | } | ||
167 | return ret.String() | ||
168 | } | ||
169 | } | ||
170 | |||
171 | // WrappedErrors is an implementation of errwrap.Wrapper so that an error-wrapped | ||
172 | // diagnostics object can be picked apart by errwrap-aware code. | ||
173 | func (dae diagnosticsAsError) WrappedErrors() []error { | ||
174 | var errs []error | ||
175 | for _, diag := range dae.Diagnostics { | ||
176 | if wrapper, isErr := diag.(nativeError); isErr { | ||
177 | errs = append(errs, wrapper.err) | ||
178 | } | ||
179 | } | ||
180 | return errs | ||
181 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/doc.go b/vendor/github.com/hashicorp/terraform/tfdiags/doc.go new file mode 100644 index 0000000..c427879 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/tfdiags/doc.go | |||
@@ -0,0 +1,16 @@ | |||
1 | // Package tfdiags is a utility package for representing errors and | ||
2 | // warnings in a manner that allows us to produce good messages for the | ||
3 | // user. | ||
4 | // | ||
5 | // "diag" is short for "diagnostics", and is meant as a general word for | ||
6 | // feedback to a user about potential or actual problems. | ||
7 | // | ||
8 | // A design goal for this package is for it to be able to provide rich | ||
9 | // messaging where possible but to also be pragmatic about dealing with | ||
10 | // generic errors produced by system components that _can't_ provide | ||
11 | // such rich messaging. As a consequence, the main types in this package -- | ||
12 | // Diagnostics and Diagnostic -- are designed so that they can be "smuggled" | ||
13 | // over an error channel and then be unpacked at the other end, so that | ||
14 | // error diagnostics (at least) can transit through APIs that are not | ||
15 | // aware of this package. | ||
16 | package tfdiags | ||
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/error.go b/vendor/github.com/hashicorp/terraform/tfdiags/error.go new file mode 100644 index 0000000..35edc30 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/tfdiags/error.go | |||
@@ -0,0 +1,23 @@ | |||
1 | package tfdiags | ||
2 | |||
3 | // nativeError is a Diagnostic implementation that wraps a normal Go error | ||
4 | type nativeError struct { | ||
5 | err error | ||
6 | } | ||
7 | |||
8 | var _ Diagnostic = nativeError{} | ||
9 | |||
10 | func (e nativeError) Severity() Severity { | ||
11 | return Error | ||
12 | } | ||
13 | |||
14 | func (e nativeError) Description() Description { | ||
15 | return Description{ | ||
16 | Summary: e.err.Error(), | ||
17 | } | ||
18 | } | ||
19 | |||
20 | func (e nativeError) Source() Source { | ||
21 | // No source information available for a native error | ||
22 | return Source{} | ||
23 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go b/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go new file mode 100644 index 0000000..24851f4 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/tfdiags/hcl.go | |||
@@ -0,0 +1,77 @@ | |||
1 | package tfdiags | ||
2 | |||
3 | import ( | ||
4 | "github.com/hashicorp/hcl2/hcl" | ||
5 | ) | ||
6 | |||
7 | // hclDiagnostic is a Diagnostic implementation that wraps a HCL Diagnostic | ||
8 | type hclDiagnostic struct { | ||
9 | diag *hcl.Diagnostic | ||
10 | } | ||
11 | |||
12 | var _ Diagnostic = hclDiagnostic{} | ||
13 | |||
14 | func (d hclDiagnostic) Severity() Severity { | ||
15 | switch d.diag.Severity { | ||
16 | case hcl.DiagWarning: | ||
17 | return Warning | ||
18 | default: | ||
19 | return Error | ||
20 | } | ||
21 | } | ||
22 | |||
23 | func (d hclDiagnostic) Description() Description { | ||
24 | return Description{ | ||
25 | Summary: d.diag.Summary, | ||
26 | Detail: d.diag.Detail, | ||
27 | } | ||
28 | } | ||
29 | |||
30 | func (d hclDiagnostic) Source() Source { | ||
31 | var ret Source | ||
32 | if d.diag.Subject != nil { | ||
33 | rng := SourceRangeFromHCL(*d.diag.Subject) | ||
34 | ret.Subject = &rng | ||
35 | } | ||
36 | if d.diag.Context != nil { | ||
37 | rng := SourceRangeFromHCL(*d.diag.Context) | ||
38 | ret.Context = &rng | ||
39 | } | ||
40 | return ret | ||
41 | } | ||
42 | |||
43 | // SourceRangeFromHCL constructs a SourceRange from the corresponding range | ||
44 | // type within the HCL package. | ||
45 | func SourceRangeFromHCL(hclRange hcl.Range) SourceRange { | ||
46 | return SourceRange{ | ||
47 | Filename: hclRange.Filename, | ||
48 | Start: SourcePos{ | ||
49 | Line: hclRange.Start.Line, | ||
50 | Column: hclRange.Start.Column, | ||
51 | Byte: hclRange.Start.Byte, | ||
52 | }, | ||
53 | End: SourcePos{ | ||
54 | Line: hclRange.End.Line, | ||
55 | Column: hclRange.End.Column, | ||
56 | Byte: hclRange.End.Byte, | ||
57 | }, | ||
58 | } | ||
59 | } | ||
60 | |||
61 | // ToHCL constructs a HCL Range from the receiving SourceRange. This is the | ||
62 | // opposite of SourceRangeFromHCL. | ||
63 | func (r SourceRange) ToHCL() hcl.Range { | ||
64 | return hcl.Range{ | ||
65 | Filename: r.Filename, | ||
66 | Start: hcl.Pos{ | ||
67 | Line: r.Start.Line, | ||
68 | Column: r.Start.Column, | ||
69 | Byte: r.Start.Byte, | ||
70 | }, | ||
71 | End: hcl.Pos{ | ||
72 | Line: r.End.Line, | ||
73 | Column: r.End.Column, | ||
74 | Byte: r.End.Byte, | ||
75 | }, | ||
76 | } | ||
77 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go b/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go new file mode 100644 index 0000000..6cc95cc --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/tfdiags/rpc_friendly.go | |||
@@ -0,0 +1,53 @@ | |||
1 | package tfdiags | ||
2 | |||
3 | import ( | ||
4 | "encoding/gob" | ||
5 | ) | ||
6 | |||
7 | type rpcFriendlyDiag struct { | ||
8 | Severity_ Severity | ||
9 | Summary_ string | ||
10 | Detail_ string | ||
11 | Subject_ *SourceRange | ||
12 | Context_ *SourceRange | ||
13 | } | ||
14 | |||
15 | // rpcFriendlyDiag transforms a given diagnostic so that is more friendly to | ||
16 | // RPC. | ||
17 | // | ||
18 | // In particular, it currently returns an object that can be serialized and | ||
19 | // later re-inflated using gob. This definition may grow to include other | ||
20 | // serializations later. | ||
21 | func makeRPCFriendlyDiag(diag Diagnostic) Diagnostic { | ||
22 | desc := diag.Description() | ||
23 | source := diag.Source() | ||
24 | return &rpcFriendlyDiag{ | ||
25 | Severity_: diag.Severity(), | ||
26 | Summary_: desc.Summary, | ||
27 | Detail_: desc.Detail, | ||
28 | Subject_: source.Subject, | ||
29 | Context_: source.Context, | ||
30 | } | ||
31 | } | ||
32 | |||
33 | func (d *rpcFriendlyDiag) Severity() Severity { | ||
34 | return d.Severity_ | ||
35 | } | ||
36 | |||
37 | func (d *rpcFriendlyDiag) Description() Description { | ||
38 | return Description{ | ||
39 | Summary: d.Summary_, | ||
40 | Detail: d.Detail_, | ||
41 | } | ||
42 | } | ||
43 | |||
44 | func (d *rpcFriendlyDiag) Source() Source { | ||
45 | return Source{ | ||
46 | Subject: d.Subject_, | ||
47 | Context: d.Context_, | ||
48 | } | ||
49 | } | ||
50 | |||
51 | func init() { | ||
52 | gob.Register((*rpcFriendlyDiag)(nil)) | ||
53 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go b/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go new file mode 100644 index 0000000..0b1249b --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/tfdiags/severity_string.go | |||
@@ -0,0 +1,21 @@ | |||
1 | // Code generated by "stringer -type=Severity"; DO NOT EDIT. | ||
2 | |||
3 | package tfdiags | ||
4 | |||
5 | import "strconv" | ||
6 | |||
7 | const ( | ||
8 | _Severity_name_0 = "Error" | ||
9 | _Severity_name_1 = "Warning" | ||
10 | ) | ||
11 | |||
12 | func (i Severity) String() string { | ||
13 | switch { | ||
14 | case i == 69: | ||
15 | return _Severity_name_0 | ||
16 | case i == 87: | ||
17 | return _Severity_name_1 | ||
18 | default: | ||
19 | return "Severity(" + strconv.FormatInt(int64(i), 10) + ")" | ||
20 | } | ||
21 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go b/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go new file mode 100644 index 0000000..fb3ac98 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/tfdiags/simple_warning.go | |||
@@ -0,0 +1,25 @@ | |||
1 | package tfdiags | ||
2 | |||
3 | type simpleWarning string | ||
4 | |||
5 | var _ Diagnostic = simpleWarning("") | ||
6 | |||
7 | // SimpleWarning constructs a simple (summary-only) warning diagnostic. | ||
8 | func SimpleWarning(msg string) Diagnostic { | ||
9 | return simpleWarning(msg) | ||
10 | } | ||
11 | |||
12 | func (e simpleWarning) Severity() Severity { | ||
13 | return Warning | ||
14 | } | ||
15 | |||
16 | func (e simpleWarning) Description() Description { | ||
17 | return Description{ | ||
18 | Summary: string(e), | ||
19 | } | ||
20 | } | ||
21 | |||
22 | func (e simpleWarning) Source() Source { | ||
23 | // No source information available for a native error | ||
24 | return Source{} | ||
25 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/tfdiags/source_range.go b/vendor/github.com/hashicorp/terraform/tfdiags/source_range.go new file mode 100644 index 0000000..3031168 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/tfdiags/source_range.go | |||
@@ -0,0 +1,35 @@ | |||
1 | package tfdiags | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "os" | ||
6 | "path/filepath" | ||
7 | ) | ||
8 | |||
9 | type SourceRange struct { | ||
10 | Filename string | ||
11 | Start, End SourcePos | ||
12 | } | ||
13 | |||
14 | type SourcePos struct { | ||
15 | Line, Column, Byte int | ||
16 | } | ||
17 | |||
18 | // StartString returns a string representation of the start of the range, | ||
19 | // including the filename and the line and column numbers. | ||
20 | func (r SourceRange) StartString() string { | ||
21 | filename := r.Filename | ||
22 | |||
23 | // We'll try to relative-ize our filename here so it's less verbose | ||
24 | // in the common case of being in the current working directory. If not, | ||
25 | // we'll just show the full path. | ||
26 | wd, err := os.Getwd() | ||
27 | if err == nil { | ||
28 | relFn, err := filepath.Rel(wd, filename) | ||
29 | if err == nil { | ||
30 | filename = relFn | ||
31 | } | ||
32 | } | ||
33 | |||
34 | return fmt.Sprintf("%s:%d,%d", filename, r.Start.Line, r.Start.Column) | ||
35 | } | ||
diff --git a/vendor/github.com/hashicorp/terraform/version/version.go b/vendor/github.com/hashicorp/terraform/version/version.go new file mode 100644 index 0000000..b21b297 --- /dev/null +++ b/vendor/github.com/hashicorp/terraform/version/version.go | |||
@@ -0,0 +1,36 @@ | |||
1 | // The version package provides a location to set the release versions for all | ||
2 | // packages to consume, without creating import cycles. | ||
3 | // | ||
4 | // This package should not import any other terraform packages. | ||
5 | package version | ||
6 | |||
7 | import ( | ||
8 | "fmt" | ||
9 | |||
10 | version "github.com/hashicorp/go-version" | ||
11 | ) | ||
12 | |||
13 | // The main version number that is being run at the moment. | ||
14 | var Version = "0.11.12" | ||
15 | |||
16 | // A pre-release marker for the version. If this is "" (empty string) | ||
17 | // then it means that it is a final release. Otherwise, this is a pre-release | ||
18 | // such as "dev" (in development), "beta", "rc1", etc. | ||
19 | var Prerelease = "dev" | ||
20 | |||
21 | // SemVer is an instance of version.Version. This has the secondary | ||
22 | // benefit of verifying during tests and init time that our version is a | ||
23 | // proper semantic version, which should always be the case. | ||
24 | var SemVer = version.Must(version.NewVersion(Version)) | ||
25 | |||
26 | // Header is the header name used to send the current terraform version | ||
27 | // in http requests. | ||
28 | const Header = "Terraform-Version" | ||
29 | |||
30 | // String returns the complete version string, including prerelease | ||
31 | func String() string { | ||
32 | if Prerelease != "" { | ||
33 | return fmt.Sprintf("%s-%s", Version, Prerelease) | ||
34 | } | ||
35 | return Version | ||
36 | } | ||