diff options
author | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
---|---|---|
committer | Nathan Dench <ndenc2@gmail.com> | 2019-05-24 15:16:44 +1000 |
commit | 107c1cdb09c575aa2f61d97f48d8587eb6bada4c (patch) | |
tree | ca7d008643efc555c388baeaf1d986e0b6b3e28c /vendor/github.com/hashicorp/terraform/helper/schema/schema.go | |
parent | 844b5a68d8af4791755b8f0ad293cc99f5959183 (diff) | |
download | terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.gz terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.tar.zst terraform-provider-statuscake-107c1cdb09c575aa2f61d97f48d8587eb6bada4c.zip |
Upgrade to 0.12
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/schema/schema.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/helper/schema/schema.go | 316 |
1 files changed, 234 insertions, 82 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go index 0ea5aad..6a3c15a 100644 --- a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go +++ b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go | |||
@@ -12,6 +12,7 @@ | |||
12 | package schema | 12 | package schema |
13 | 13 | ||
14 | import ( | 14 | import ( |
15 | "context" | ||
15 | "fmt" | 16 | "fmt" |
16 | "os" | 17 | "os" |
17 | "reflect" | 18 | "reflect" |
@@ -19,7 +20,9 @@ import ( | |||
19 | "sort" | 20 | "sort" |
20 | "strconv" | 21 | "strconv" |
21 | "strings" | 22 | "strings" |
23 | "sync" | ||
22 | 24 | ||
25 | "github.com/hashicorp/terraform/config" | ||
23 | "github.com/hashicorp/terraform/terraform" | 26 | "github.com/hashicorp/terraform/terraform" |
24 | "github.com/mitchellh/copystructure" | 27 | "github.com/mitchellh/copystructure" |
25 | "github.com/mitchellh/mapstructure" | 28 | "github.com/mitchellh/mapstructure" |
@@ -31,6 +34,27 @@ const PanicOnErr = "TF_SCHEMA_PANIC_ON_ERROR" | |||
31 | // type used for schema package context keys | 34 | // type used for schema package context keys |
32 | type contextKey string | 35 | type contextKey string |
33 | 36 | ||
37 | var ( | ||
38 | protoVersionMu sync.Mutex | ||
39 | protoVersion5 = false | ||
40 | ) | ||
41 | |||
42 | func isProto5() bool { | ||
43 | protoVersionMu.Lock() | ||
44 | defer protoVersionMu.Unlock() | ||
45 | return protoVersion5 | ||
46 | |||
47 | } | ||
48 | |||
49 | // SetProto5 enables a feature flag for any internal changes required required | ||
50 | // to work with the new plugin protocol. This should not be called by | ||
51 | // provider. | ||
52 | func SetProto5() { | ||
53 | protoVersionMu.Lock() | ||
54 | defer protoVersionMu.Unlock() | ||
55 | protoVersion5 = true | ||
56 | } | ||
57 | |||
34 | // Schema is used to describe the structure of a value. | 58 | // Schema is used to describe the structure of a value. |
35 | // | 59 | // |
36 | // Read the documentation of the struct elements for important details. | 60 | // Read the documentation of the struct elements for important details. |
@@ -51,6 +75,26 @@ type Schema struct { | |||
51 | // | 75 | // |
52 | Type ValueType | 76 | Type ValueType |
53 | 77 | ||
78 | // ConfigMode allows for overriding the default behaviors for mapping | ||
79 | // schema entries onto configuration constructs. | ||
80 | // | ||
81 | // By default, the Elem field is used to choose whether a particular | ||
82 | // schema is represented in configuration as an attribute or as a nested | ||
83 | // block; if Elem is a *schema.Resource then it's a block and it's an | ||
84 | // attribute otherwise. | ||
85 | // | ||
86 | // If Elem is *schema.Resource then setting ConfigMode to | ||
87 | // SchemaConfigModeAttr will force it to be represented in configuration | ||
88 | // as an attribute, which means that the Computed flag can be used to | ||
89 | // provide default elements when the argument isn't set at all, while still | ||
90 | // allowing the user to force zero elements by explicitly assigning an | ||
91 | // empty list. | ||
92 | // | ||
93 | // When Computed is set without Optional, the attribute is not settable | ||
94 | // in configuration at all and so SchemaConfigModeAttr is the automatic | ||
95 | // behavior, and SchemaConfigModeBlock is not permitted. | ||
96 | ConfigMode SchemaConfigMode | ||
97 | |||
54 | // If one of these is set, then this item can come from the configuration. | 98 | // If one of these is set, then this item can come from the configuration. |
55 | // Both cannot be set. If Optional is set, the value is optional. If | 99 | // Both cannot be set. If Optional is set, the value is optional. If |
56 | // Required is set, the value is required. | 100 | // Required is set, the value is required. |
@@ -123,7 +167,8 @@ type Schema struct { | |||
123 | // The following fields are only set for a TypeList, TypeSet, or TypeMap. | 167 | // The following fields are only set for a TypeList, TypeSet, or TypeMap. |
124 | // | 168 | // |
125 | // Elem represents the element type. For a TypeMap, it must be a *Schema | 169 | // Elem represents the element type. For a TypeMap, it must be a *Schema |
126 | // with a Type of TypeString, otherwise it may be either a *Schema or a | 170 | // with a Type that is one of the primitives: TypeString, TypeBool, |
171 | // TypeInt, or TypeFloat. Otherwise it may be either a *Schema or a | ||
127 | // *Resource. If it is *Schema, the element type is just a simple value. | 172 | // *Resource. If it is *Schema, the element type is just a simple value. |
128 | // If it is *Resource, the element type is a complex structure, | 173 | // If it is *Resource, the element type is a complex structure, |
129 | // potentially with its own lifecycle. | 174 | // potentially with its own lifecycle. |
@@ -141,13 +186,17 @@ type Schema struct { | |||
141 | // used to wrap a complex structure, however less than one instance would | 186 | // used to wrap a complex structure, however less than one instance would |
142 | // cause instability. | 187 | // cause instability. |
143 | // | 188 | // |
144 | // PromoteSingle, if true, will allow single elements to be standalone | 189 | // If the field Optional is set to true then MinItems is ignored and thus |
145 | // and promote them to a list. For example "foo" would be promoted to | 190 | // effectively zero. |
146 | // ["foo"] automatically. This is primarily for legacy reasons and the | 191 | MaxItems int |
147 | // ambiguity is not recommended for new usage. Promotion is only allowed | 192 | MinItems int |
148 | // for primitive element types. | 193 | |
149 | MaxItems int | 194 | // PromoteSingle originally allowed for a single element to be assigned |
150 | MinItems int | 195 | // where a primitive list was expected, but this no longer works from |
196 | // Terraform v0.12 onwards (Terraform Core will require a list to be set | ||
197 | // regardless of what this is set to) and so only applies to Terraform v0.11 | ||
198 | // and earlier, and so should be used only to retain this functionality | ||
199 | // for those still using v0.11 with a provider that formerly used this. | ||
151 | PromoteSingle bool | 200 | PromoteSingle bool |
152 | 201 | ||
153 | // The following fields are only valid for a TypeSet type. | 202 | // The following fields are only valid for a TypeSet type. |
@@ -189,7 +238,8 @@ type Schema struct { | |||
189 | // guaranteed to be of the proper Schema type, and it can yield warnings or | 238 | // guaranteed to be of the proper Schema type, and it can yield warnings or |
190 | // errors based on inspection of that value. | 239 | // errors based on inspection of that value. |
191 | // | 240 | // |
192 | // ValidateFunc currently only works for primitive types. | 241 | // ValidateFunc is honored only when the schema's Type is set to TypeInt, |
242 | // TypeFloat, TypeString, TypeBool, or TypeMap. It is ignored for all other types. | ||
193 | ValidateFunc SchemaValidateFunc | 243 | ValidateFunc SchemaValidateFunc |
194 | 244 | ||
195 | // Sensitive ensures that the attribute's value does not get displayed in | 245 | // Sensitive ensures that the attribute's value does not get displayed in |
@@ -199,6 +249,17 @@ type Schema struct { | |||
199 | Sensitive bool | 249 | Sensitive bool |
200 | } | 250 | } |
201 | 251 | ||
252 | // SchemaConfigMode is used to influence how a schema item is mapped into a | ||
253 | // corresponding configuration construct, using the ConfigMode field of | ||
254 | // Schema. | ||
255 | type SchemaConfigMode int | ||
256 | |||
257 | const ( | ||
258 | SchemaConfigModeAuto SchemaConfigMode = iota | ||
259 | SchemaConfigModeAttr | ||
260 | SchemaConfigModeBlock | ||
261 | ) | ||
262 | |||
202 | // SchemaDiffSuppressFunc is a function which can be used to determine | 263 | // SchemaDiffSuppressFunc is a function which can be used to determine |
203 | // whether a detected diff on a schema element is "valid" or not, and | 264 | // whether a detected diff on a schema element is "valid" or not, and |
204 | // suppress it from the plan if necessary. | 265 | // suppress it from the plan if necessary. |
@@ -364,6 +425,11 @@ func (s *Schema) finalizeDiff(d *terraform.ResourceAttrDiff, customized bool) *t | |||
364 | return d | 425 | return d |
365 | } | 426 | } |
366 | 427 | ||
428 | // InternalMap is used to aid in the transition to the new schema types and | ||
429 | // protocol. The name is not meant to convey any usefulness, as this is not to | ||
430 | // be used directly by any providers. | ||
431 | type InternalMap = schemaMap | ||
432 | |||
367 | // schemaMap is a wrapper that adds nice functions on top of schemas. | 433 | // schemaMap is a wrapper that adds nice functions on top of schemas. |
368 | type schemaMap map[string]*Schema | 434 | type schemaMap map[string]*Schema |
369 | 435 | ||
@@ -404,7 +470,8 @@ func (m schemaMap) Diff( | |||
404 | s *terraform.InstanceState, | 470 | s *terraform.InstanceState, |
405 | c *terraform.ResourceConfig, | 471 | c *terraform.ResourceConfig, |
406 | customizeDiff CustomizeDiffFunc, | 472 | customizeDiff CustomizeDiffFunc, |
407 | meta interface{}) (*terraform.InstanceDiff, error) { | 473 | meta interface{}, |
474 | handleRequiresNew bool) (*terraform.InstanceDiff, error) { | ||
408 | result := new(terraform.InstanceDiff) | 475 | result := new(terraform.InstanceDiff) |
409 | result.Attributes = make(map[string]*terraform.ResourceAttrDiff) | 476 | result.Attributes = make(map[string]*terraform.ResourceAttrDiff) |
410 | 477 | ||
@@ -450,82 +517,85 @@ func (m schemaMap) Diff( | |||
450 | } | 517 | } |
451 | } | 518 | } |
452 | 519 | ||
453 | // If the diff requires a new resource, then we recompute the diff | 520 | if handleRequiresNew { |
454 | // so we have the complete new resource diff, and preserve the | 521 | // If the diff requires a new resource, then we recompute the diff |
455 | // RequiresNew fields where necessary so the user knows exactly what | 522 | // so we have the complete new resource diff, and preserve the |
456 | // caused that. | 523 | // RequiresNew fields where necessary so the user knows exactly what |
457 | if result.RequiresNew() { | 524 | // caused that. |
458 | // Create the new diff | 525 | if result.RequiresNew() { |
459 | result2 := new(terraform.InstanceDiff) | 526 | // Create the new diff |
460 | result2.Attributes = make(map[string]*terraform.ResourceAttrDiff) | 527 | result2 := new(terraform.InstanceDiff) |
461 | 528 | result2.Attributes = make(map[string]*terraform.ResourceAttrDiff) | |
462 | // Preserve the DestroyTainted flag | ||
463 | result2.DestroyTainted = result.DestroyTainted | ||
464 | 529 | ||
465 | // Reset the data to not contain state. We have to call init() | 530 | // Preserve the DestroyTainted flag |
466 | // again in order to reset the FieldReaders. | 531 | result2.DestroyTainted = result.DestroyTainted |
467 | d.state = nil | ||
468 | d.init() | ||
469 | 532 | ||
470 | // Perform the diff again | 533 | // Reset the data to not contain state. We have to call init() |
471 | for k, schema := range m { | 534 | // again in order to reset the FieldReaders. |
472 | err := m.diff(k, schema, result2, d, false) | 535 | d.state = nil |
473 | if err != nil { | 536 | d.init() |
474 | return nil, err | ||
475 | } | ||
476 | } | ||
477 | 537 | ||
478 | // Re-run customization | 538 | // Perform the diff again |
479 | if !result2.DestroyTainted && customizeDiff != nil { | 539 | for k, schema := range m { |
480 | mc := m.DeepCopy() | 540 | err := m.diff(k, schema, result2, d, false) |
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 { | 541 | if err != nil { |
488 | return nil, err | 542 | return nil, err |
489 | } | 543 | } |
490 | } | 544 | } |
491 | } | ||
492 | 545 | ||
493 | // Force all the fields to not force a new since we know what we | 546 | // Re-run customization |
494 | // want to force new. | 547 | if !result2.DestroyTainted && customizeDiff != nil { |
495 | for k, attr := range result2.Attributes { | 548 | mc := m.DeepCopy() |
496 | if attr == nil { | 549 | rd := newResourceDiff(mc, c, d.state, result2) |
497 | continue | 550 | if err := customizeDiff(rd, meta); err != nil { |
551 | return nil, err | ||
552 | } | ||
553 | for _, k := range rd.UpdatedKeys() { | ||
554 | err := m.diff(k, mc[k], result2, rd, false) | ||
555 | if err != nil { | ||
556 | return nil, err | ||
557 | } | ||
558 | } | ||
498 | } | 559 | } |
499 | 560 | ||
500 | if attr.RequiresNew { | 561 | // Force all the fields to not force a new since we know what we |
501 | attr.RequiresNew = false | 562 | // want to force new. |
502 | } | 563 | for k, attr := range result2.Attributes { |
564 | if attr == nil { | ||
565 | continue | ||
566 | } | ||
503 | 567 | ||
504 | if s != nil { | 568 | if attr.RequiresNew { |
505 | attr.Old = s.Attributes[k] | 569 | attr.RequiresNew = false |
506 | } | 570 | } |
507 | } | ||
508 | 571 | ||
509 | // Now copy in all the requires new diffs... | 572 | if s != nil { |
510 | for k, attr := range result.Attributes { | 573 | attr.Old = s.Attributes[k] |
511 | if attr == nil { | 574 | } |
512 | continue | ||
513 | } | 575 | } |
514 | 576 | ||
515 | newAttr, ok := result2.Attributes[k] | 577 | // Now copy in all the requires new diffs... |
516 | if !ok { | 578 | for k, attr := range result.Attributes { |
517 | newAttr = attr | 579 | if attr == nil { |
518 | } | 580 | continue |
581 | } | ||
519 | 582 | ||
520 | if attr.RequiresNew { | 583 | newAttr, ok := result2.Attributes[k] |
521 | newAttr.RequiresNew = true | 584 | if !ok { |
585 | newAttr = attr | ||
586 | } | ||
587 | |||
588 | if attr.RequiresNew { | ||
589 | newAttr.RequiresNew = true | ||
590 | } | ||
591 | |||
592 | result2.Attributes[k] = newAttr | ||
522 | } | 593 | } |
523 | 594 | ||
524 | result2.Attributes[k] = newAttr | 595 | // And set the diff! |
596 | result = result2 | ||
525 | } | 597 | } |
526 | 598 | ||
527 | // And set the diff! | ||
528 | result = result2 | ||
529 | } | 599 | } |
530 | 600 | ||
531 | // Go through and detect all of the ComputedWhens now that we've | 601 | // Go through and detect all of the ComputedWhens now that we've |
@@ -611,6 +681,10 @@ func (m schemaMap) Validate(c *terraform.ResourceConfig) ([]string, []error) { | |||
611 | // from a unit test (and not in user-path code) to verify that a schema | 681 | // from a unit test (and not in user-path code) to verify that a schema |
612 | // is properly built. | 682 | // is properly built. |
613 | func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { | 683 | func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { |
684 | return m.internalValidate(topSchemaMap, false) | ||
685 | } | ||
686 | |||
687 | func (m schemaMap) internalValidate(topSchemaMap schemaMap, attrsOnly bool) error { | ||
614 | if topSchemaMap == nil { | 688 | if topSchemaMap == nil { |
615 | topSchemaMap = m | 689 | topSchemaMap = m |
616 | } | 690 | } |
@@ -631,6 +705,34 @@ func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { | |||
631 | return fmt.Errorf("%s: One of optional, required, or computed must be set", k) | 705 | return fmt.Errorf("%s: One of optional, required, or computed must be set", k) |
632 | } | 706 | } |
633 | 707 | ||
708 | computedOnly := v.Computed && !v.Optional | ||
709 | |||
710 | switch v.ConfigMode { | ||
711 | case SchemaConfigModeBlock: | ||
712 | if _, ok := v.Elem.(*Resource); !ok { | ||
713 | return fmt.Errorf("%s: ConfigMode of block is allowed only when Elem is *schema.Resource", k) | ||
714 | } | ||
715 | if attrsOnly { | ||
716 | return fmt.Errorf("%s: ConfigMode of block cannot be used in child of schema with ConfigMode of attribute", k) | ||
717 | } | ||
718 | if computedOnly { | ||
719 | return fmt.Errorf("%s: ConfigMode of block cannot be used for computed schema", k) | ||
720 | } | ||
721 | case SchemaConfigModeAttr: | ||
722 | // anything goes | ||
723 | case SchemaConfigModeAuto: | ||
724 | // Since "Auto" for Elem: *Resource would create a nested block, | ||
725 | // and that's impossible inside an attribute, we require it to be | ||
726 | // explicitly overridden as mode "Attr" for clarity. | ||
727 | if _, ok := v.Elem.(*Resource); ok { | ||
728 | if attrsOnly { | ||
729 | return fmt.Errorf("%s: in *schema.Resource with ConfigMode of attribute, so must also have ConfigMode of attribute", k) | ||
730 | } | ||
731 | } | ||
732 | default: | ||
733 | return fmt.Errorf("%s: invalid ConfigMode value", k) | ||
734 | } | ||
735 | |||
634 | if v.Computed && v.Default != nil { | 736 | if v.Computed && v.Default != nil { |
635 | return fmt.Errorf("%s: Default must be nil if computed", k) | 737 | return fmt.Errorf("%s: Default must be nil if computed", k) |
636 | } | 738 | } |
@@ -695,7 +797,9 @@ func (m schemaMap) InternalValidate(topSchemaMap schemaMap) error { | |||
695 | 797 | ||
696 | switch t := v.Elem.(type) { | 798 | switch t := v.Elem.(type) { |
697 | case *Resource: | 799 | case *Resource: |
698 | if err := t.InternalValidate(topSchemaMap, true); err != nil { | 800 | attrsOnly := attrsOnly || v.ConfigMode == SchemaConfigModeAttr |
801 | |||
802 | if err := schemaMap(t.Schema).internalValidate(topSchemaMap, attrsOnly); err != nil { | ||
699 | return err | 803 | return err |
700 | } | 804 | } |
701 | case *Schema: | 805 | case *Schema: |
@@ -785,10 +889,19 @@ func (m schemaMap) diff( | |||
785 | for attrK, attrV := range unsupressedDiff.Attributes { | 889 | for attrK, attrV := range unsupressedDiff.Attributes { |
786 | switch rd := d.(type) { | 890 | switch rd := d.(type) { |
787 | case *ResourceData: | 891 | case *ResourceData: |
788 | if schema.DiffSuppressFunc != nil && | 892 | if schema.DiffSuppressFunc != nil && attrV != nil && |
789 | attrV != nil && | ||
790 | schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, rd) { | 893 | schema.DiffSuppressFunc(attrK, attrV.Old, attrV.New, rd) { |
791 | continue | 894 | // If this attr diff is suppressed, we may still need it in the |
895 | // overall diff if it's contained within a set. Rather than | ||
896 | // dropping the diff, make it a NOOP. | ||
897 | if !all { | ||
898 | continue | ||
899 | } | ||
900 | |||
901 | attrV = &terraform.ResourceAttrDiff{ | ||
902 | Old: attrV.Old, | ||
903 | New: attrV.Old, | ||
904 | } | ||
792 | } | 905 | } |
793 | } | 906 | } |
794 | diff.Attributes[attrK] = attrV | 907 | diff.Attributes[attrK] = attrV |
@@ -1171,7 +1284,7 @@ func (m schemaMap) diffString( | |||
1171 | return fmt.Errorf("%s: %s", k, err) | 1284 | return fmt.Errorf("%s: %s", k, err) |
1172 | } | 1285 | } |
1173 | 1286 | ||
1174 | if os == ns && !all { | 1287 | if os == ns && !all && !computed { |
1175 | // They're the same value. If there old value is not blank or we | 1288 | // They're the same value. If there old value is not blank or we |
1176 | // have an ID, then return right away since we're already setup. | 1289 | // have an ID, then return right away since we're already setup. |
1177 | if os != "" || d.Id() != "" { | 1290 | if os != "" || d.Id() != "" { |
@@ -1179,7 +1292,7 @@ func (m schemaMap) diffString( | |||
1179 | } | 1292 | } |
1180 | 1293 | ||
1181 | // Otherwise, only continue if we're computed | 1294 | // Otherwise, only continue if we're computed |
1182 | if !schema.Computed && !computed { | 1295 | if !schema.Computed { |
1183 | return nil | 1296 | return nil |
1184 | } | 1297 | } |
1185 | } | 1298 | } |
@@ -1210,7 +1323,7 @@ func (m schemaMap) inputString( | |||
1210 | input terraform.UIInput, | 1323 | input terraform.UIInput, |
1211 | k string, | 1324 | k string, |
1212 | schema *Schema) (interface{}, error) { | 1325 | schema *Schema) (interface{}, error) { |
1213 | result, err := input.Input(&terraform.InputOpts{ | 1326 | result, err := input.Input(context.Background(), &terraform.InputOpts{ |
1214 | Id: k, | 1327 | Id: k, |
1215 | Query: k, | 1328 | Query: k, |
1216 | Description: schema.Description, | 1329 | Description: schema.Description, |
@@ -1252,6 +1365,13 @@ func (m schemaMap) validate( | |||
1252 | "%q: this field cannot be set", k)} | 1365 | "%q: this field cannot be set", k)} |
1253 | } | 1366 | } |
1254 | 1367 | ||
1368 | if raw == config.UnknownVariableValue { | ||
1369 | // If the value is unknown then we can't validate it yet. | ||
1370 | // In particular, this avoids spurious type errors where downstream | ||
1371 | // validation code sees UnknownVariableValue as being just a string. | ||
1372 | return nil, nil | ||
1373 | } | ||
1374 | |||
1255 | err := m.validateConflictingAttributes(k, schema, c) | 1375 | err := m.validateConflictingAttributes(k, schema, c) |
1256 | if err != nil { | 1376 | if err != nil { |
1257 | return nil, []error{err} | 1377 | return nil, []error{err} |
@@ -1269,10 +1389,15 @@ func (m schemaMap) validateConflictingAttributes( | |||
1269 | return nil | 1389 | return nil |
1270 | } | 1390 | } |
1271 | 1391 | ||
1272 | for _, conflicting_key := range schema.ConflictsWith { | 1392 | for _, conflictingKey := range schema.ConflictsWith { |
1273 | if _, ok := c.Get(conflicting_key); ok { | 1393 | if raw, ok := c.Get(conflictingKey); ok { |
1394 | if raw == config.UnknownVariableValue { | ||
1395 | // An unknown value might become unset (null) once known, so | ||
1396 | // we must defer validation until it's known. | ||
1397 | continue | ||
1398 | } | ||
1274 | return fmt.Errorf( | 1399 | return fmt.Errorf( |
1275 | "%q: conflicts with %s", k, conflicting_key) | 1400 | "%q: conflicts with %s", k, conflictingKey) |
1276 | } | 1401 | } |
1277 | } | 1402 | } |
1278 | 1403 | ||
@@ -1284,6 +1409,13 @@ func (m schemaMap) validateList( | |||
1284 | raw interface{}, | 1409 | raw interface{}, |
1285 | schema *Schema, | 1410 | schema *Schema, |
1286 | c *terraform.ResourceConfig) ([]string, []error) { | 1411 | c *terraform.ResourceConfig) ([]string, []error) { |
1412 | // first check if the list is wholly unknown | ||
1413 | if s, ok := raw.(string); ok { | ||
1414 | if s == config.UnknownVariableValue { | ||
1415 | return nil, nil | ||
1416 | } | ||
1417 | } | ||
1418 | |||
1287 | // We use reflection to verify the slice because you can't | 1419 | // We use reflection to verify the slice because you can't |
1288 | // case to []interface{} unless the slice is exactly that type. | 1420 | // case to []interface{} unless the slice is exactly that type. |
1289 | rawV := reflect.ValueOf(raw) | 1421 | rawV := reflect.ValueOf(raw) |
@@ -1355,6 +1487,13 @@ func (m schemaMap) validateMap( | |||
1355 | raw interface{}, | 1487 | raw interface{}, |
1356 | schema *Schema, | 1488 | schema *Schema, |
1357 | c *terraform.ResourceConfig) ([]string, []error) { | 1489 | c *terraform.ResourceConfig) ([]string, []error) { |
1490 | // first check if the list is wholly unknown | ||
1491 | if s, ok := raw.(string); ok { | ||
1492 | if s == config.UnknownVariableValue { | ||
1493 | return nil, nil | ||
1494 | } | ||
1495 | } | ||
1496 | |||
1358 | // We use reflection to verify the slice because you can't | 1497 | // We use reflection to verify the slice because you can't |
1359 | // case to []interface{} unless the slice is exactly that type. | 1498 | // case to []interface{} unless the slice is exactly that type. |
1360 | rawV := reflect.ValueOf(raw) | 1499 | rawV := reflect.ValueOf(raw) |
@@ -1556,12 +1695,25 @@ func (m schemaMap) validatePrimitive( | |||
1556 | } | 1695 | } |
1557 | decoded = n | 1696 | decoded = n |
1558 | case TypeInt: | 1697 | case TypeInt: |
1559 | // Verify that we can parse this as an int | 1698 | switch { |
1560 | var n int | 1699 | case isProto5(): |
1561 | if err := mapstructure.WeakDecode(raw, &n); err != nil { | 1700 | // We need to verify the type precisely, because WeakDecode will |
1562 | return nil, []error{fmt.Errorf("%s: %s", k, err)} | 1701 | // decode a float as an integer. |
1702 | |||
1703 | // the config shims only use int for integral number values | ||
1704 | if v, ok := raw.(int); ok { | ||
1705 | decoded = v | ||
1706 | } else { | ||
1707 | return nil, []error{fmt.Errorf("%s: must be a whole number, got %v", k, raw)} | ||
1708 | } | ||
1709 | default: | ||
1710 | // Verify that we can parse this as an int | ||
1711 | var n int | ||
1712 | if err := mapstructure.WeakDecode(raw, &n); err != nil { | ||
1713 | return nil, []error{fmt.Errorf("%s: %s", k, err)} | ||
1714 | } | ||
1715 | decoded = n | ||
1563 | } | 1716 | } |
1564 | decoded = n | ||
1565 | case TypeFloat: | 1717 | case TypeFloat: |
1566 | // Verify that we can parse this as an int | 1718 | // Verify that we can parse this as an int |
1567 | var n float64 | 1719 | var n float64 |