aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/schema/resource.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/schema/resource.go270
1 files changed, 263 insertions, 7 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
index d3be2d6..b5e3065 100644
--- a/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
+++ b/vendor/github.com/hashicorp/terraform/helper/schema/resource.go
@@ -8,6 +8,7 @@ import (
8 8
9 "github.com/hashicorp/terraform/config" 9 "github.com/hashicorp/terraform/config"
10 "github.com/hashicorp/terraform/terraform" 10 "github.com/hashicorp/terraform/terraform"
11 "github.com/zclconf/go-cty/cty"
11) 12)
12 13
13// Resource represents a thing in Terraform that has a set of configurable 14// Resource represents a thing in Terraform that has a set of configurable
@@ -44,6 +45,12 @@ type Resource struct {
44 // their Versioning at any integer >= 1 45 // their Versioning at any integer >= 1
45 SchemaVersion int 46 SchemaVersion int
46 47
48 // MigrateState is deprecated and any new changes to a resource's schema
49 // should be handled by StateUpgraders. Existing MigrateState implementations
50 // should remain for compatibility with existing state. MigrateState will
51 // still be called if the stored SchemaVersion is less than the
52 // first version of the StateUpgraders.
53 //
47 // MigrateState is responsible for updating an InstanceState with an old 54 // MigrateState is responsible for updating an InstanceState with an old
48 // version to the format expected by the current version of the Schema. 55 // version to the format expected by the current version of the Schema.
49 // 56 //
@@ -56,6 +63,18 @@ type Resource struct {
56 // needs to make any remote API calls. 63 // needs to make any remote API calls.
57 MigrateState StateMigrateFunc 64 MigrateState StateMigrateFunc
58 65
66 // StateUpgraders contains the functions responsible for upgrading an
67 // existing state with an old schema version to a newer schema. It is
68 // called specifically by Terraform when the stored schema version is less
69 // than the current SchemaVersion of the Resource.
70 //
71 // StateUpgraders map specific schema versions to a StateUpgrader
72 // function. The registered versions are expected to be ordered,
73 // consecutive values. The initial value may be greater than 0 to account
74 // for legacy schemas that weren't recorded and can be handled by
75 // MigrateState.
76 StateUpgraders []StateUpgrader
77
59 // The functions below are the CRUD operations for this resource. 78 // The functions below are the CRUD operations for this resource.
60 // 79 //
61 // The only optional operation is Update. If Update is not implemented, 80 // The only optional operation is Update. If Update is not implemented,
@@ -136,6 +155,27 @@ type Resource struct {
136 Timeouts *ResourceTimeout 155 Timeouts *ResourceTimeout
137} 156}
138 157
158// ShimInstanceStateFromValue converts a cty.Value to a
159// terraform.InstanceState.
160func (r *Resource) ShimInstanceStateFromValue(state cty.Value) (*terraform.InstanceState, error) {
161 // Get the raw shimmed value. While this is correct, the set hashes don't
162 // match those from the Schema.
163 s := terraform.NewInstanceStateShimmedFromValue(state, r.SchemaVersion)
164
165 // We now rebuild the state through the ResourceData, so that the set indexes
166 // match what helper/schema expects.
167 data, err := schemaMap(r.Schema).Data(s, nil)
168 if err != nil {
169 return nil, err
170 }
171
172 s = data.State()
173 if s == nil {
174 s = &terraform.InstanceState{}
175 }
176 return s, nil
177}
178
139// See Resource documentation. 179// See Resource documentation.
140type CreateFunc func(*ResourceData, interface{}) error 180type CreateFunc func(*ResourceData, interface{}) error
141 181
@@ -155,6 +195,27 @@ type ExistsFunc func(*ResourceData, interface{}) (bool, error)
155type StateMigrateFunc func( 195type StateMigrateFunc func(
156 int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error) 196 int, *terraform.InstanceState, interface{}) (*terraform.InstanceState, error)
157 197
198type StateUpgrader struct {
199 // Version is the version schema that this Upgrader will handle, converting
200 // it to Version+1.
201 Version int
202
203 // Type describes the schema that this function can upgrade. Type is
204 // required to decode the schema if the state was stored in a legacy
205 // flatmap format.
206 Type cty.Type
207
208 // Upgrade takes the JSON encoded state and the provider meta value, and
209 // upgrades the state one single schema version. The provided state is
210 // deocded into the default json types using a map[string]interface{}. It
211 // is up to the StateUpgradeFunc to ensure that the returned value can be
212 // encoded using the new schema.
213 Upgrade StateUpgradeFunc
214}
215
216// See StateUpgrader
217type StateUpgradeFunc func(rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error)
218
158// See Resource documentation. 219// See Resource documentation.
159type CustomizeDiffFunc func(*ResourceDiff, interface{}) error 220type CustomizeDiffFunc func(*ResourceDiff, interface{}) error
160 221
@@ -247,7 +308,7 @@ func (r *Resource) Diff(
247 return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err) 308 return nil, fmt.Errorf("[ERR] Error decoding timeout: %s", err)
248 } 309 }
249 310
250 instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta) 311 instanceDiff, err := schemaMap(r.Schema).Diff(s, c, r.CustomizeDiff, meta, true)
251 if err != nil { 312 if err != nil {
252 return instanceDiff, err 313 return instanceDiff, err
253 } 314 }
@@ -263,6 +324,45 @@ func (r *Resource) Diff(
263 return instanceDiff, err 324 return instanceDiff, err
264} 325}
265 326
327func (r *Resource) simpleDiff(
328 s *terraform.InstanceState,
329 c *terraform.ResourceConfig,
330 meta interface{}) (*terraform.InstanceDiff, error) {
331
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)
340 if err != nil {
341 return instanceDiff, err
342 }
343
344 if instanceDiff == nil {
345 log.Printf("[DEBUG] Instance Diff is nil in SimpleDiff()")
346 return nil, err
347 }
348
349 // Make sure the old value is set in each of the instance diffs.
350 // This was done by the RequiresNew logic in the full legacy Diff.
351 for k, attr := range instanceDiff.Attributes {
352 if attr == nil {
353 continue
354 }
355 if s != nil {
356 attr.Old = s.Attributes[k]
357 }
358 }
359
360 if err := t.DiffEncode(instanceDiff); err != nil {
361 log.Printf("[ERR] Error encoding timeout to instance diff: %s", err)
362 }
363 return instanceDiff, err
364}
365
266// Validate validates the resource configuration against the schema. 366// Validate validates the resource configuration against the schema.
267func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) { 367func (r *Resource) Validate(c *terraform.ResourceConfig) ([]string, []error) {
268 warns, errs := schemaMap(r.Schema).Validate(c) 368 warns, errs := schemaMap(r.Schema).Validate(c)
@@ -300,8 +400,11 @@ func (r *Resource) ReadDataApply(
300 return r.recordCurrentSchemaVersion(state), err 400 return r.recordCurrentSchemaVersion(state), err
301} 401}
302 402
303// Refresh refreshes the state of the resource. 403// RefreshWithoutUpgrade reads the instance state, but does not call
304func (r *Resource) Refresh( 404// MigrateState or the StateUpgraders, since those are now invoked in a
405// separate API call.
406// RefreshWithoutUpgrade is part of the new plugin shims.
407func (r *Resource) RefreshWithoutUpgrade(
305 s *terraform.InstanceState, 408 s *terraform.InstanceState,
306 meta interface{}) (*terraform.InstanceState, error) { 409 meta interface{}) (*terraform.InstanceState, error) {
307 // If the ID is already somehow blank, it doesn't exist 410 // If the ID is already somehow blank, it doesn't exist
@@ -335,12 +438,60 @@ func (r *Resource) Refresh(
335 } 438 }
336 } 439 }
337 440
338 needsMigration, stateSchemaVersion := r.checkSchemaVersion(s) 441 data, err := schemaMap(r.Schema).Data(s, nil)
339 if needsMigration && r.MigrateState != nil { 442 data.timeouts = &rt
340 s, err := r.MigrateState(stateSchemaVersion, s, meta) 443 if err != nil {
444 return s, err
445 }
446
447 err = r.Read(data, meta)
448 state := data.State()
449 if state != nil && state.ID == "" {
450 state = nil
451 }
452
453 return r.recordCurrentSchemaVersion(state), err
454}
455
456// Refresh refreshes the state of the resource.
457func (r *Resource) Refresh(
458 s *terraform.InstanceState,
459 meta interface{}) (*terraform.InstanceState, error) {
460 // If the ID is already somehow blank, it doesn't exist
461 if s.ID == "" {
462 return nil, nil
463 }
464
465 rt := ResourceTimeout{}
466 if _, ok := s.Meta[TimeoutKey]; ok {
467 if err := rt.StateDecode(s); err != nil {
468 log.Printf("[ERR] Error decoding ResourceTimeout: %s", err)
469 }
470 }
471
472 if r.Exists != nil {
473 // Make a copy of data so that if it is modified it doesn't
474 // affect our Read later.
475 data, err := schemaMap(r.Schema).Data(s, nil)
476 data.timeouts = &rt
477
341 if err != nil { 478 if err != nil {
342 return s, err 479 return s, err
343 } 480 }
481
482 exists, err := r.Exists(data, meta)
483 if err != nil {
484 return s, err
485 }
486 if !exists {
487 return nil, nil
488 }
489 }
490
491 // there may be new StateUpgraders that need to be run
492 s, err := r.upgradeState(s, meta)
493 if err != nil {
494 return s, err
344 } 495 }
345 496
346 data, err := schemaMap(r.Schema).Data(s, nil) 497 data, err := schemaMap(r.Schema).Data(s, nil)
@@ -358,6 +509,71 @@ func (r *Resource) Refresh(
358 return r.recordCurrentSchemaVersion(state), err 509 return r.recordCurrentSchemaVersion(state), err
359} 510}
360 511
512func (r *Resource) upgradeState(s *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
513 var err error
514
515 needsMigration, stateSchemaVersion := r.checkSchemaVersion(s)
516 migrate := needsMigration && r.MigrateState != nil
517
518 if migrate {
519 s, err = r.MigrateState(stateSchemaVersion, s, meta)
520 if err != nil {
521 return s, err
522 }
523 }
524
525 if len(r.StateUpgraders) == 0 {
526 return s, nil
527 }
528
529 // If we ran MigrateState, then the stateSchemaVersion value is no longer
530 // correct. We can expect the first upgrade function to be the correct
531 // schema type version.
532 if migrate {
533 stateSchemaVersion = r.StateUpgraders[0].Version
534 }
535
536 schemaType := r.CoreConfigSchema().ImpliedType()
537 // find the expected type to convert the state
538 for _, upgrader := range r.StateUpgraders {
539 if stateSchemaVersion == upgrader.Version {
540 schemaType = upgrader.Type
541 }
542 }
543
544 // StateUpgraders only operate on the new JSON format state, so the state
545 // need to be converted.
546 stateVal, err := StateValueFromInstanceState(s, schemaType)
547 if err != nil {
548 return nil, err
549 }
550
551 jsonState, err := StateValueToJSONMap(stateVal, schemaType)
552 if err != nil {
553 return nil, err
554 }
555
556 for _, upgrader := range r.StateUpgraders {
557 if stateSchemaVersion != upgrader.Version {
558 continue
559 }
560
561 jsonState, err = upgrader.Upgrade(jsonState, meta)
562 if err != nil {
563 return nil, err
564 }
565 stateSchemaVersion++
566 }
567
568 // now we need to re-flatmap the new state
569 stateVal, err = JSONMapToStateValue(jsonState, r.CoreConfigSchema())
570 if err != nil {
571 return nil, err
572 }
573
574 return r.ShimInstanceStateFromValue(stateVal)
575}
576
361// InternalValidate should be called to validate the structure 577// InternalValidate should be called to validate the structure
362// of the resource. 578// of the resource.
363// 579//
@@ -437,6 +653,31 @@ func (r *Resource) InternalValidate(topSchemaMap schemaMap, writable bool) error
437 } 653 }
438 } 654 }
439 655
656 lastVersion := -1
657 for _, u := range r.StateUpgraders {
658 if lastVersion >= 0 && u.Version-lastVersion > 1 {
659 return fmt.Errorf("missing schema version between %d and %d", lastVersion, u.Version)
660 }
661
662 if u.Version >= r.SchemaVersion {
663 return fmt.Errorf("StateUpgrader version %d is >= current version %d", u.Version, r.SchemaVersion)
664 }
665
666 if !u.Type.IsObjectType() {
667 return fmt.Errorf("StateUpgrader %d type is not cty.Object", u.Version)
668 }
669
670 if u.Upgrade == nil {
671 return fmt.Errorf("StateUpgrader %d missing StateUpgradeFunc", u.Version)
672 }
673
674 lastVersion = u.Version
675 }
676
677 if lastVersion >= 0 && lastVersion != r.SchemaVersion-1 {
678 return fmt.Errorf("missing StateUpgrader between %d and %d", lastVersion, r.SchemaVersion)
679 }
680
440 // Data source 681 // Data source
441 if r.isTopLevel() && !writable { 682 if r.isTopLevel() && !writable {
442 tsm = schemaMap(r.Schema) 683 tsm = schemaMap(r.Schema)
@@ -513,6 +754,13 @@ func (r *Resource) TestResourceData() *ResourceData {
513 } 754 }
514} 755}
515 756
757// SchemasForFlatmapPath tries its best to find a sequence of schemas that
758// the given dot-delimited attribute path traverses through in the schema
759// of the receiving Resource.
760func (r *Resource) SchemasForFlatmapPath(path string) []*Schema {
761 return SchemasForFlatmapPath(path, r.Schema)
762}
763
516// Returns true if the resource is "top level" i.e. not a sub-resource. 764// Returns true if the resource is "top level" i.e. not a sub-resource.
517func (r *Resource) isTopLevel() bool { 765func (r *Resource) isTopLevel() bool {
518 // TODO: This is a heuristic; replace with a definitive attribute? 766 // TODO: This is a heuristic; replace with a definitive attribute?
@@ -538,7 +786,15 @@ func (r *Resource) checkSchemaVersion(is *terraform.InstanceState) (bool, int) {
538 } 786 }
539 787
540 stateSchemaVersion, _ := strconv.Atoi(rawString) 788 stateSchemaVersion, _ := strconv.Atoi(rawString)
541 return stateSchemaVersion < r.SchemaVersion, stateSchemaVersion 789
790 // Don't run MigrateState if the version is handled by a StateUpgrader,
791 // since StateMigrateFuncs are not required to handle unknown versions
792 maxVersion := r.SchemaVersion
793 if len(r.StateUpgraders) > 0 {
794 maxVersion = r.StateUpgraders[0].Version
795 }
796
797 return stateSchemaVersion < maxVersion, stateSchemaVersion
542} 798}
543 799
544func (r *Resource) recordCurrentSchemaVersion( 800func (r *Resource) recordCurrentSchemaVersion(