aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/helper
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper')
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider.go78
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/state_shim.go78
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go3
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader_config.go3
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/field_reader_diff.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource.go22
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go4
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/schema.go75
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/shims.go4
9 files changed, 204 insertions, 67 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider.go b/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider.go
index 510f47f..104c8f5 100644
--- a/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider.go
+++ b/vendor/github.com/hashicorp/terraform/helper/plugin/grpc_provider.go
@@ -2,7 +2,6 @@ package plugin
2 2
3import ( 3import (
4 "encoding/json" 4 "encoding/json"
5 "errors"
6 "fmt" 5 "fmt"
7 "log" 6 "log"
8 "strconv" 7 "strconv"
@@ -16,6 +15,7 @@ import (
16 "github.com/hashicorp/terraform/configs/configschema" 15 "github.com/hashicorp/terraform/configs/configschema"
17 "github.com/hashicorp/terraform/helper/schema" 16 "github.com/hashicorp/terraform/helper/schema"
18 proto "github.com/hashicorp/terraform/internal/tfplugin5" 17 proto "github.com/hashicorp/terraform/internal/tfplugin5"
18 "github.com/hashicorp/terraform/plans/objchange"
19 "github.com/hashicorp/terraform/plugin/convert" 19 "github.com/hashicorp/terraform/plugin/convert"
20 "github.com/hashicorp/terraform/terraform" 20 "github.com/hashicorp/terraform/terraform"
21) 21)
@@ -284,6 +284,17 @@ func (s *GRPCProviderServer) UpgradeResourceState(_ context.Context, req *proto.
284 return resp, nil 284 return resp, nil
285 } 285 }
286 286
287 // Now we need to make sure blocks are represented correctly, which means
288 // that missing blocks are empty collections, rather than null.
289 // First we need to CoerceValue to ensure that all object types match.
290 val, err = schemaBlock.CoerceValue(val)
291 if err != nil {
292 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
293 return resp, nil
294 }
295 // Normalize the value and fill in any missing blocks.
296 val = objchange.NormalizeObjectFromLegacySDK(val, schemaBlock)
297
287 // encode the final state to the expected msgpack format 298 // encode the final state to the expected msgpack format
288 newStateMP, err := msgpack.Marshal(val, schemaBlock.ImpliedType()) 299 newStateMP, err := msgpack.Marshal(val, schemaBlock.ImpliedType())
289 if err != nil { 300 if err != nil {
@@ -316,11 +327,15 @@ func (s *GRPCProviderServer) upgradeFlatmapState(version int, m map[string]strin
316 requiresMigrate = version < res.StateUpgraders[0].Version 327 requiresMigrate = version < res.StateUpgraders[0].Version
317 } 328 }
318 329
319 if requiresMigrate { 330 if requiresMigrate && res.MigrateState == nil {
320 if res.MigrateState == nil { 331 // Providers were previously allowed to bump the version
321 return nil, 0, errors.New("cannot upgrade state, missing MigrateState function") 332 // without declaring MigrateState.
333 // If there are further upgraders, then we've only updated that far.
334 if len(res.StateUpgraders) > 0 {
335 schemaType = res.StateUpgraders[0].Type
336 upgradedVersion = res.StateUpgraders[0].Version
322 } 337 }
323 338 } else if requiresMigrate {
324 is := &terraform.InstanceState{ 339 is := &terraform.InstanceState{
325 ID: m["id"], 340 ID: m["id"],
326 Attributes: m, 341 Attributes: m,
@@ -476,7 +491,12 @@ func (s *GRPCProviderServer) Configure(_ context.Context, req *proto.Configure_R
476} 491}
477 492
478func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadResource_Request) (*proto.ReadResource_Response, error) { 493func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadResource_Request) (*proto.ReadResource_Response, error) {
479 resp := &proto.ReadResource_Response{} 494 resp := &proto.ReadResource_Response{
495 // helper/schema did previously handle private data during refresh, but
496 // core is now going to expect this to be maintained in order to
497 // persist it in the state.
498 Private: req.Private,
499 }
480 500
481 res := s.provider.ResourcesMap[req.TypeName] 501 res := s.provider.ResourcesMap[req.TypeName]
482 schemaBlock := s.getResourceSchemaBlock(req.TypeName) 502 schemaBlock := s.getResourceSchemaBlock(req.TypeName)
@@ -493,6 +513,15 @@ func (s *GRPCProviderServer) ReadResource(_ context.Context, req *proto.ReadReso
493 return resp, nil 513 return resp, nil
494 } 514 }
495 515
516 private := make(map[string]interface{})
517 if len(req.Private) > 0 {
518 if err := json.Unmarshal(req.Private, &private); err != nil {
519 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
520 return resp, nil
521 }
522 }
523 instanceState.Meta = private
524
496 newInstanceState, err := res.RefreshWithoutUpgrade(instanceState, s.provider.Meta()) 525 newInstanceState, err := res.RefreshWithoutUpgrade(instanceState, s.provider.Meta())
497 if err != nil { 526 if err != nil {
498 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 527 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
@@ -569,6 +598,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
569 // We don't usually plan destroys, but this can return early in any case. 598 // We don't usually plan destroys, but this can return early in any case.
570 if proposedNewStateVal.IsNull() { 599 if proposedNewStateVal.IsNull() {
571 resp.PlannedState = req.ProposedNewState 600 resp.PlannedState = req.ProposedNewState
601 resp.PlannedPrivate = req.PriorPrivate
572 return resp, nil 602 return resp, nil
573 } 603 }
574 604
@@ -623,6 +653,7 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
623 // description that _shows_ there are no changes. This is always the 653 // description that _shows_ there are no changes. This is always the
624 // prior state, because we force a diff above if this is a new instance. 654 // prior state, because we force a diff above if this is a new instance.
625 resp.PlannedState = req.PriorState 655 resp.PlannedState = req.PriorState
656 resp.PlannedPrivate = req.PriorPrivate
626 return resp, nil 657 return resp, nil
627 } 658 }
628 659
@@ -683,6 +714,18 @@ func (s *GRPCProviderServer) PlanResourceChange(_ context.Context, req *proto.Pl
683 Msgpack: plannedMP, 714 Msgpack: plannedMP,
684 } 715 }
685 716
717 // encode any timeouts into the diff Meta
718 t := &schema.ResourceTimeout{}
719 if err := t.ConfigDecode(res, cfg); err != nil {
720 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
721 return resp, nil
722 }
723
724 if err := t.DiffEncode(diff); err != nil {
725 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
726 return resp, nil
727 }
728
686 // Now we need to store any NewExtra values, which are where any actual 729 // Now we need to store any NewExtra values, which are where any actual
687 // StateFunc modified config fields are hidden. 730 // StateFunc modified config fields are hidden.
688 privateMap := diff.Meta 731 privateMap := diff.Meta
@@ -929,6 +972,9 @@ func (s *GRPCProviderServer) ImportResourceState(_ context.Context, req *proto.I
929 return resp, nil 972 return resp, nil
930 } 973 }
931 974
975 // Normalize the value and fill in any missing blocks.
976 newStateVal = objchange.NormalizeObjectFromLegacySDK(newStateVal, schemaBlock)
977
932 newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType()) 978 newStateMP, err := msgpack.Marshal(newStateVal, schemaBlock.ImpliedType())
933 if err != nil { 979 if err != nil {
934 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err) 980 resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
@@ -1160,6 +1206,8 @@ func normalizeNullValues(dst, src cty.Value, apply bool) cty.Value {
1160 } 1206 }
1161 } 1207 }
1162 1208
1209 // check the invariants that we need below, to ensure we are working with
1210 // non-null and known values.
1163 if src.IsNull() || !src.IsKnown() || !dst.IsKnown() { 1211 if src.IsNull() || !src.IsKnown() || !dst.IsKnown() {
1164 return dst 1212 return dst
1165 } 1213 }
@@ -1278,8 +1326,12 @@ func normalizeNullValues(dst, src cty.Value, apply bool) cty.Value {
1278 return cty.ListVal(dsts) 1326 return cty.ListVal(dsts)
1279 } 1327 }
1280 1328
1281 case ty.IsPrimitiveType(): 1329 case ty == cty.String:
1282 if dst.IsNull() && src.IsWhollyKnown() && apply { 1330 // The legacy SDK should not be able to remove a value during plan or
1331 // apply, however we are only going to overwrite this if the source was
1332 // an empty string, since that is what is often equated with unset and
1333 // lost in the diff process.
1334 if dst.IsNull() && src.AsString() == "" {
1283 return src 1335 return src
1284 } 1336 }
1285 } 1337 }
@@ -1305,11 +1357,19 @@ func validateConfigNulls(v cty.Value, path cty.Path) []*proto.Diagnostic {
1305 for it.Next() { 1357 for it.Next() {
1306 kv, ev := it.Element() 1358 kv, ev := it.Element()
1307 if ev.IsNull() { 1359 if ev.IsNull() {
1360 // if this is a set, the kv is also going to be null which
1361 // isn't a valid path element, so we can't append it to the
1362 // diagnostic.
1363 p := path
1364 if !kv.IsNull() {
1365 p = append(p, cty.IndexStep{Key: kv})
1366 }
1367
1308 diags = append(diags, &proto.Diagnostic{ 1368 diags = append(diags, &proto.Diagnostic{
1309 Severity: proto.Diagnostic_ERROR, 1369 Severity: proto.Diagnostic_ERROR,
1310 Summary: "Null value found in list", 1370 Summary: "Null value found in list",
1311 Detail: "Null values are not allowed for this attribute value.", 1371 Detail: "Null values are not allowed for this attribute value.",
1312 Attribute: convert.PathToAttributePath(append(path, cty.IndexStep{Key: kv})), 1372 Attribute: convert.PathToAttributePath(p),
1313 }) 1373 })
1314 continue 1374 continue
1315 } 1375 }
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/state_shim.go b/vendor/github.com/hashicorp/terraform/helper/resource/state_shim.go
index b2aff99..f488207 100644
--- a/vendor/github.com/hashicorp/terraform/helper/resource/state_shim.go
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/state_shim.go
@@ -1,6 +1,7 @@
1package resource 1package resource
2 2
3import ( 3import (
4 "encoding/json"
4 "fmt" 5 "fmt"
5 6
6 "github.com/hashicorp/terraform/addrs" 7 "github.com/hashicorp/terraform/addrs"
@@ -52,43 +53,57 @@ func shimNewState(newState *states.State, providers map[string]terraform.Resourc
52 resource := getResource(providers, providerType, res.Addr) 53 resource := getResource(providers, providerType, res.Addr)
53 54
54 for key, i := range res.Instances { 55 for key, i := range res.Instances {
55 flatmap, err := shimmedAttributes(i.Current, resource) 56 resState := &terraform.ResourceState{
56 if err != nil { 57 Type: resType,
57 return nil, fmt.Errorf("error decoding state for %q: %s", resType, err) 58 Provider: res.ProviderConfig.String(),
58 } 59 }
59 60
60 resState := &terraform.ResourceState{ 61 // We should always have a Current instance here, but be safe about checking.
61 Type: resType, 62 if i.Current != nil {
62 Primary: &terraform.InstanceState{ 63 flatmap, err := shimmedAttributes(i.Current, resource)
64 if err != nil {
65 return nil, fmt.Errorf("error decoding state for %q: %s", resType, err)
66 }
67
68 var meta map[string]interface{}
69 if i.Current.Private != nil {
70 err := json.Unmarshal(i.Current.Private, &meta)
71 if err != nil {
72 return nil, err
73 }
74 }
75
76 resState.Primary = &terraform.InstanceState{
63 ID: flatmap["id"], 77 ID: flatmap["id"],
64 Attributes: flatmap, 78 Attributes: flatmap,
65 Tainted: i.Current.Status == states.ObjectTainted, 79 Tainted: i.Current.Status == states.ObjectTainted,
66 }, 80 Meta: meta,
67 Provider: res.ProviderConfig.String(),
68 }
69 if i.Current.SchemaVersion != 0 {
70 resState.Primary.Meta = map[string]interface{}{
71 "schema_version": i.Current.SchemaVersion,
72 } 81 }
73 }
74 82
75 for _, dep := range i.Current.Dependencies { 83 if i.Current.SchemaVersion != 0 {
76 resState.Dependencies = append(resState.Dependencies, dep.String()) 84 resState.Primary.Meta = map[string]interface{}{
77 } 85 "schema_version": i.Current.SchemaVersion,
86 }
87 }
78 88
79 // convert the indexes to the old style flapmap indexes 89 for _, dep := range i.Current.Dependencies {
80 idx := "" 90 resState.Dependencies = append(resState.Dependencies, dep.String())
81 switch key.(type) {
82 case addrs.IntKey:
83 // don't add numeric index values to resources with a count of 0
84 if len(res.Instances) > 1 {
85 idx = fmt.Sprintf(".%d", key)
86 } 91 }
87 case addrs.StringKey:
88 idx = "." + key.String()
89 }
90 92
91 mod.Resources[res.Addr.String()+idx] = resState 93 // convert the indexes to the old style flapmap indexes
94 idx := ""
95 switch key.(type) {
96 case addrs.IntKey:
97 // don't add numeric index values to resources with a count of 0
98 if len(res.Instances) > 1 {
99 idx = fmt.Sprintf(".%d", key)
100 }
101 case addrs.StringKey:
102 idx = "." + key.String()
103 }
104
105 mod.Resources[res.Addr.String()+idx] = resState
106 }
92 107
93 // add any deposed instances 108 // add any deposed instances
94 for _, dep := range i.Deposed { 109 for _, dep := range i.Deposed {
@@ -97,10 +112,19 @@ func shimNewState(newState *states.State, providers map[string]terraform.Resourc
97 return nil, fmt.Errorf("error decoding deposed state for %q: %s", resType, err) 112 return nil, fmt.Errorf("error decoding deposed state for %q: %s", resType, err)
98 } 113 }
99 114
115 var meta map[string]interface{}
116 if dep.Private != nil {
117 err := json.Unmarshal(dep.Private, &meta)
118 if err != nil {
119 return nil, err
120 }
121 }
122
100 deposed := &terraform.InstanceState{ 123 deposed := &terraform.InstanceState{
101 ID: flatmap["id"], 124 ID: flatmap["id"],
102 Attributes: flatmap, 125 Attributes: flatmap,
103 Tainted: dep.Status == states.ObjectTainted, 126 Tainted: dep.Status == states.ObjectTainted,
127 Meta: meta,
104 } 128 }
105 if dep.SchemaVersion != 0 { 129 if dep.SchemaVersion != 0 {
106 deposed.Meta = map[string]interface{}{ 130 deposed.Meta = map[string]interface{}{
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 311fdb6..f34e17a 100644
--- a/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go
+++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing_config.go
@@ -10,7 +10,6 @@ import (
10 "strings" 10 "strings"
11 11
12 "github.com/hashicorp/terraform/addrs" 12 "github.com/hashicorp/terraform/addrs"
13 "github.com/hashicorp/terraform/config"
14 "github.com/hashicorp/terraform/config/hcl2shim" 13 "github.com/hashicorp/terraform/config/hcl2shim"
15 "github.com/hashicorp/terraform/states" 14 "github.com/hashicorp/terraform/states"
16 15
@@ -341,7 +340,7 @@ func legacyDiffComparisonString(changes *plans.Changes) string {
341 v := newAttrs[attrK] 340 v := newAttrs[attrK]
342 u := oldAttrs[attrK] 341 u := oldAttrs[attrK]
343 342
344 if v == config.UnknownVariableValue { 343 if v == hcl2shim.UnknownVariableValue {
345 v = "<computed>" 344 v = "<computed>"
346 } 345 }
347 // NOTE: we don't support <sensitive> here because we would 346 // NOTE: we don't support <sensitive> here because we would
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 808375c..6ad3f13 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
@@ -219,6 +219,9 @@ func (r *ConfigFieldReader) readMap(k string, schema *Schema) (FieldReadResult,
219 v, _ := r.Config.Get(key) 219 v, _ := r.Config.Get(key)
220 result[ik] = v 220 result[ik] = v
221 } 221 }
222 case nil:
223 // the map may have been empty on the configuration, so we leave the
224 // empty result
222 default: 225 default:
223 panic(fmt.Sprintf("unknown type: %#v", mraw)) 226 panic(fmt.Sprintf("unknown type: %#v", mraw))
224 } 227 }
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 ae35b4a..3e70acf 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
@@ -95,7 +95,9 @@ func (r *DiffFieldReader) readMap(
95 return FieldReadResult{}, err 95 return FieldReadResult{}, err
96 } 96 }
97 if source.Exists { 97 if source.Exists {
98 result = source.Value.(map[string]interface{}) 98 // readMap may return a nil value, or an unknown value placeholder in
99 // some cases, causing the type assertion to panic if we don't assign the ok value
100 result, _ = source.Value.(map[string]interface{})
99 resultSet = true 101 resultSet = true
100 } 102 }
101 103
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
index b5e3065..b59e4e8 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
@@ -95,9 +95,10 @@ type Resource struct {
95 // 95 //
96 // Exists is a function that is called to check if a resource still 96 // Exists is a function that is called to check if a resource still
97 // exists. If this returns false, then this will affect the diff 97 // exists. If this returns false, then this will affect the diff
98 // accordingly. If this function isn't set, it will not be called. It 98 // accordingly. If this function isn't set, it will not be called. You
99 // is highly recommended to set it. The *ResourceData passed to Exists 99 // can also signal existence in the Read method by calling d.SetId("")
100 // should _not_ be modified. 100 // if the Resource is no longer present and should be removed from state.
101 // The *ResourceData passed to Exists should _not_ be modified.
101 Create CreateFunc 102 Create CreateFunc
102 Read ReadFunc 103 Read ReadFunc
103 Update UpdateFunc 104 Update UpdateFunc
@@ -329,21 +330,13 @@ func (r *Resource) simpleDiff(
329 c *terraform.ResourceConfig, 330 c *terraform.ResourceConfig,
330 meta interface{}) (*terraform.InstanceDiff, error) { 331 meta interface{}) (*terraform.InstanceDiff, error) {
331 332
332 t := &ResourceTimeout{}
333 err := t.ConfigDecode(r, c)
334
335 if err != nil {
336 return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err)
337 }
338
339 instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, false) 333 instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, false)
340 if err != nil { 334 if err != nil {
341 return instanceDiff, err 335 return instanceDiff, err
342 } 336 }
343 337
344 if instanceDiff == nil { 338 if instanceDiff == nil {
345 log.Printf("[DEBUG] Instance Diff is nil in SimpleDiff()") 339 instanceDiff = terraform.NewInstanceDiff()
346 return nil, err
347 } 340 }
348 341
349 // Make sure the old value is set in each of the instance diffs. 342 // Make sure the old value is set in each of the instance diffs.
@@ -357,10 +350,7 @@ func (r *Resource) simpleDiff(
357 } 350 }
358 } 351 }
359 352
360 if err := t.DiffEncode(instanceDiff); err != nil { 353 return instanceDiff, nil
361 log.Printf("[ERR] Error encoding timeout to instance diff: %s", err)
362 }
363 return instanceDiff, err
364} 354}
365 355
366// Validate validates the resource configuration against the schema. 356// Validate validates the resource configuration against the schema.
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go
index 9e422c1..222b2cc 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource_timeout.go
@@ -5,7 +5,7 @@ import (
5 "log" 5 "log"
6 "time" 6 "time"
7 7
8 "github.com/hashicorp/terraform/config" 8 "github.com/hashicorp/terraform/config/hcl2shim"
9 "github.com/hashicorp/terraform/terraform" 9 "github.com/hashicorp/terraform/terraform"
10 "github.com/mitchellh/copystructure" 10 "github.com/mitchellh/copystructure"
11) 11)
@@ -70,7 +70,7 @@ func (t *ResourceTimeout) ConfigDecode(s *Resource, c *terraform.ResourceConfig)
70 case []map[string]interface{}: 70 case []map[string]interface{}:
71 rawTimeouts = raw 71 rawTimeouts = raw
72 case string: 72 case string:
73 if raw == config.UnknownVariableValue { 73 if raw == hcl2shim.UnknownVariableValue {
74 // Timeout is not defined in the config 74 // Timeout is not defined in the config
75 // Defaults will be used instead 75 // Defaults will be used instead
76 return nil 76 return nil
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go
index 6a3c15a..bcc8e4b 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/schema.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/schema.go
@@ -22,7 +22,7 @@ import (
22 "strings" 22 "strings"
23 "sync" 23 "sync"
24 24
25 "github.com/hashicorp/terraform/config" 25 "github.com/hashicorp/terraform/config/hcl2shim"
26 "github.com/hashicorp/terraform/terraform" 26 "github.com/hashicorp/terraform/terraform"
27 "github.com/mitchellh/copystructure" 27 "github.com/mitchellh/copystructure"
28 "github.com/mitchellh/mapstructure" 28 "github.com/mitchellh/mapstructure"
@@ -1365,10 +1365,15 @@ func (m schemaMap) validate(
1365 "%q: this field cannot be set", k)} 1365 "%q: this field cannot be set", k)}
1366 } 1366 }
1367 1367
1368 if raw == config.UnknownVariableValue { 1368 // If the value is unknown then we can't validate it yet.
1369 // If the value is unknown then we can't validate it yet. 1369 // In particular, this avoids spurious type errors where downstream
1370 // In particular, this avoids spurious type errors where downstream 1370 // validation code sees UnknownVariableValue as being just a string.
1371 // validation code sees UnknownVariableValue as being just a string. 1371 // The SDK has to allow the unknown value through initially, so that
1372 // Required fields set via an interpolated value are accepted.
1373 if !isWhollyKnown(raw) {
1374 if schema.Deprecated != "" {
1375 return []string{fmt.Sprintf("%q: [DEPRECATED] %s", k, schema.Deprecated)}, nil
1376 }
1372 return nil, nil 1377 return nil, nil
1373 } 1378 }
1374 1379
@@ -1380,6 +1385,28 @@ func (m schemaMap) validate(
1380 return m.validateType(k, raw, schema, c) 1385 return m.validateType(k, raw, schema, c)
1381} 1386}
1382 1387
1388// isWhollyKnown returns false if the argument contains an UnknownVariableValue
1389func isWhollyKnown(raw interface{}) bool {
1390 switch raw := raw.(type) {
1391 case string:
1392 if raw == hcl2shim.UnknownVariableValue {
1393 return false
1394 }
1395 case []interface{}:
1396 for _, v := range raw {
1397 if !isWhollyKnown(v) {
1398 return false
1399 }
1400 }
1401 case map[string]interface{}:
1402 for _, v := range raw {
1403 if !isWhollyKnown(v) {
1404 return false
1405 }
1406 }
1407 }
1408 return true
1409}
1383func (m schemaMap) validateConflictingAttributes( 1410func (m schemaMap) validateConflictingAttributes(
1384 k string, 1411 k string,
1385 schema *Schema, 1412 schema *Schema,
@@ -1391,7 +1418,7 @@ func (m schemaMap) validateConflictingAttributes(
1391 1418
1392 for _, conflictingKey := range schema.ConflictsWith { 1419 for _, conflictingKey := range schema.ConflictsWith {
1393 if raw, ok := c.Get(conflictingKey); ok { 1420 if raw, ok := c.Get(conflictingKey); ok {
1394 if raw == config.UnknownVariableValue { 1421 if raw == hcl2shim.UnknownVariableValue {
1395 // An unknown value might become unset (null) once known, so 1422 // An unknown value might become unset (null) once known, so
1396 // we must defer validation until it's known. 1423 // we must defer validation until it's known.
1397 continue 1424 continue
@@ -1411,11 +1438,16 @@ func (m schemaMap) validateList(
1411 c *terraform.ResourceConfig) ([]string, []error) { 1438 c *terraform.ResourceConfig) ([]string, []error) {
1412 // first check if the list is wholly unknown 1439 // first check if the list is wholly unknown
1413 if s, ok := raw.(string); ok { 1440 if s, ok := raw.(string); ok {
1414 if s == config.UnknownVariableValue { 1441 if s == hcl2shim.UnknownVariableValue {
1415 return nil, nil 1442 return nil, nil
1416 } 1443 }
1417 } 1444 }
1418 1445
1446 // schemaMap can't validate nil
1447 if raw == nil {
1448 return nil, nil
1449 }
1450
1419 // We use reflection to verify the slice because you can't 1451 // We use reflection to verify the slice because you can't
1420 // case to []interface{} unless the slice is exactly that type. 1452 // case to []interface{} unless the slice is exactly that type.
1421 rawV := reflect.ValueOf(raw) 1453 rawV := reflect.ValueOf(raw)
@@ -1432,6 +1464,15 @@ func (m schemaMap) validateList(
1432 "%s: should be a list", k)} 1464 "%s: should be a list", k)}
1433 } 1465 }
1434 1466
1467 // We can't validate list length if this came from a dynamic block.
1468 // Since there's no way to determine if something was from a dynamic block
1469 // at this point, we're going to skip validation in the new protocol if
1470 // there are any unknowns. Validate will eventually be called again once
1471 // all values are known.
1472 if isProto5() && !isWhollyKnown(raw) {
1473 return nil, nil
1474 }
1475
1435 // Validate length 1476 // Validate length
1436 if schema.MaxItems > 0 && rawV.Len() > schema.MaxItems { 1477 if schema.MaxItems > 0 && rawV.Len() > schema.MaxItems {
1437 return nil, []error{fmt.Errorf( 1478 return nil, []error{fmt.Errorf(
@@ -1489,11 +1530,15 @@ func (m schemaMap) validateMap(
1489 c *terraform.ResourceConfig) ([]string, []error) { 1530 c *terraform.ResourceConfig) ([]string, []error) {
1490 // first check if the list is wholly unknown 1531 // first check if the list is wholly unknown
1491 if s, ok := raw.(string); ok { 1532 if s, ok := raw.(string); ok {
1492 if s == config.UnknownVariableValue { 1533 if s == hcl2shim.UnknownVariableValue {
1493 return nil, nil 1534 return nil, nil
1494 } 1535 }
1495 } 1536 }
1496 1537
1538 // schemaMap can't validate nil
1539 if raw == nil {
1540 return nil, nil
1541 }
1497 // We use reflection to verify the slice because you can't 1542 // We use reflection to verify the slice because you can't
1498 // case to []interface{} unless the slice is exactly that type. 1543 // case to []interface{} unless the slice is exactly that type.
1499 rawV := reflect.ValueOf(raw) 1544 rawV := reflect.ValueOf(raw)
@@ -1620,6 +1665,12 @@ func (m schemaMap) validateObject(
1620 schema map[string]*Schema, 1665 schema map[string]*Schema,
1621 c *terraform.ResourceConfig) ([]string, []error) { 1666 c *terraform.ResourceConfig) ([]string, []error) {
1622 raw, _ := c.Get(k) 1667 raw, _ := c.Get(k)
1668
1669 // schemaMap can't validate nil
1670 if raw == nil {
1671 return nil, nil
1672 }
1673
1623 if _, ok := raw.(map[string]interface{}); !ok && !c.IsComputed(k) { 1674 if _, ok := raw.(map[string]interface{}); !ok && !c.IsComputed(k) {
1624 return nil, []error{fmt.Errorf( 1675 return nil, []error{fmt.Errorf(
1625 "%s: expected object, got %s", 1676 "%s: expected object, got %s",
@@ -1664,6 +1715,14 @@ func (m schemaMap) validatePrimitive(
1664 raw interface{}, 1715 raw interface{},
1665 schema *Schema, 1716 schema *Schema,
1666 c *terraform.ResourceConfig) ([]string, []error) { 1717 c *terraform.ResourceConfig) ([]string, []error) {
1718
1719 // a nil value shouldn't happen in the old protocol, and in the new
1720 // protocol the types have already been validated. Either way, we can't
1721 // reflect on nil, so don't panic.
1722 if raw == nil {
1723 return nil, nil
1724 }
1725
1667 // Catch if the user gave a complex type where a primitive was 1726 // Catch if the user gave a complex type where a primitive was
1668 // expected, so we can return a friendly error message that 1727 // expected, so we can return a friendly error message that
1669 // doesn't contain Go type system terminology. 1728 // doesn't contain Go type system terminology.
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/shims.go b/vendor/github.com/hashicorp/terraform/helper/schema/shims.go
index 203d017..988573e 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/shims.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/shims.go
@@ -6,7 +6,7 @@ import (
6 "github.com/zclconf/go-cty/cty" 6 "github.com/zclconf/go-cty/cty"
7 ctyjson "github.com/zclconf/go-cty/cty/json" 7 ctyjson "github.com/zclconf/go-cty/cty/json"
8 8
9 "github.com/hashicorp/terraform/config" 9 "github.com/hashicorp/terraform/config/hcl2shim"
10 "github.com/hashicorp/terraform/configs/configschema" 10 "github.com/hashicorp/terraform/configs/configschema"
11 "github.com/hashicorp/terraform/terraform" 11 "github.com/hashicorp/terraform/terraform"
12) 12)
@@ -50,7 +50,7 @@ func removeConfigUnknowns(cfg map[string]interface{}) {
50 for k, v := range cfg { 50 for k, v := range cfg {
51 switch v := v.(type) { 51 switch v := v.(type) {
52 case string: 52 case string:
53 if v == config.UnknownVariableValue { 53 if v == hcl2shim.UnknownVariableValue {
54 delete(cfg, k) 54 delete(cfg, k)
55 } 55 }
56 case []interface{}: 56 case []interface{}: