github.com/bengesoff/terraform@v0.3.1-0.20141018223233-b25a53629922/helper/schema/resource_data.go (about) 1 package schema 2 3 import ( 4 "fmt" 5 "reflect" 6 "strconv" 7 "strings" 8 "sync" 9 10 "github.com/hashicorp/terraform/terraform" 11 "github.com/mitchellh/mapstructure" 12 ) 13 14 // ResourceData is used to query and set the attributes of a resource. 15 // 16 // ResourceData is the primary argument received for CRUD operations on 17 // a resource as well as configuration of a provider. It is a powerful 18 // structure that can be used to not only query data, but check for changes, 19 // define partial state updates, etc. 20 // 21 // The most relevant methods to take a look at are Get, Set, and Partial. 22 type ResourceData struct { 23 // Settable (internally) 24 schema map[string]*Schema 25 config *terraform.ResourceConfig 26 state *terraform.InstanceState 27 diff *terraform.InstanceDiff 28 diffing bool 29 30 // Don't set 31 setMap map[string]string 32 newState *terraform.InstanceState 33 partial bool 34 partialMap map[string]struct{} 35 once sync.Once 36 } 37 38 // getSource represents the level we want to get for a value (internally). 39 // Any source less than or equal to the level will be loaded (whichever 40 // has a value first). 41 type getSource byte 42 43 const ( 44 getSourceState getSource = 1 << iota 45 getSourceConfig 46 getSourceDiff 47 getSourceSet 48 getSourceExact 49 getSourceMax = getSourceSet 50 ) 51 52 // getResult is the internal structure that is generated when a Get 53 // is called that contains some extra data that might be used. 54 type getResult struct { 55 Value interface{} 56 ValueProcessed interface{} 57 Computed bool 58 Exists bool 59 Schema *Schema 60 } 61 62 var getResultEmpty getResult 63 64 // Get returns the data for the given key, or nil if the key doesn't exist 65 // in the schema. 66 // 67 // If the key does exist in the schema but doesn't exist in the configuration, 68 // then the default value for that type will be returned. For strings, this is 69 // "", for numbers it is 0, etc. 70 // 71 // If you want to test if something is set at all in the configuration, 72 // use GetOk. 73 func (d *ResourceData) Get(key string) interface{} { 74 v, _ := d.GetOk(key) 75 return v 76 } 77 78 // GetChange returns the old and new value for a given key. 79 // 80 // HasChange should be used to check if a change exists. It is possible 81 // that both the old and new value are the same if the old value was not 82 // set and the new value is. This is common, for example, for boolean 83 // fields which have a zero value of false. 84 func (d *ResourceData) GetChange(key string) (interface{}, interface{}) { 85 o, n := d.getChange(key, getSourceConfig, getSourceDiff) 86 return o.Value, n.Value 87 } 88 89 // GetOk returns the data for the given key and whether or not the key 90 // existed or not in the configuration. The second boolean result will also 91 // be false if a key is given that isn't in the schema at all. 92 // 93 // The first result will not necessarilly be nil if the value doesn't exist. 94 // The second result should be checked to determine this information. 95 func (d *ResourceData) GetOk(key string) (interface{}, bool) { 96 r := d.getRaw(key, getSourceSet) 97 return r.Value, r.Exists 98 } 99 100 func (d *ResourceData) getRaw(key string, level getSource) getResult { 101 var parts []string 102 if key != "" { 103 parts = strings.Split(key, ".") 104 } 105 106 return d.getObject("", parts, d.schema, level) 107 } 108 109 // HasChange returns whether or not the given key has been changed. 110 func (d *ResourceData) HasChange(key string) bool { 111 o, n := d.GetChange(key) 112 return !reflect.DeepEqual(o, n) 113 } 114 115 // Partial turns partial state mode on/off. 116 // 117 // When partial state mode is enabled, then only key prefixes specified 118 // by SetPartial will be in the final state. This allows providers to return 119 // partial states for partially applied resources (when errors occur). 120 func (d *ResourceData) Partial(on bool) { 121 d.partial = on 122 if on { 123 if d.partialMap == nil { 124 d.partialMap = make(map[string]struct{}) 125 } 126 } else { 127 d.partialMap = nil 128 } 129 } 130 131 // Set sets the value for the given key. 132 // 133 // If the key is invalid or the value is not a correct type, an error 134 // will be returned. 135 func (d *ResourceData) Set(key string, value interface{}) error { 136 if d.setMap == nil { 137 d.setMap = make(map[string]string) 138 } 139 140 parts := strings.Split(key, ".") 141 return d.setObject("", parts, d.schema, value) 142 } 143 144 // SetPartial adds the key prefix to the final state output while 145 // in partial state mode. 146 // 147 // If partial state mode is disabled, then this has no effect. Additionally, 148 // whenever partial state mode is toggled, the partial data is cleared. 149 func (d *ResourceData) SetPartial(k string) { 150 if d.partial { 151 d.partialMap[k] = struct{}{} 152 } 153 } 154 155 // Id returns the ID of the resource. 156 func (d *ResourceData) Id() string { 157 var result string 158 159 if d.state != nil { 160 result = d.state.ID 161 } 162 163 if d.newState != nil { 164 result = d.newState.ID 165 } 166 167 return result 168 } 169 170 // ConnInfo returns the connection info for this resource. 171 func (d *ResourceData) ConnInfo() map[string]string { 172 if d.newState != nil { 173 return d.newState.Ephemeral.ConnInfo 174 } 175 176 if d.state != nil { 177 return d.state.Ephemeral.ConnInfo 178 } 179 180 return nil 181 } 182 183 // SetId sets the ID of the resource. If the value is blank, then the 184 // resource is destroyed. 185 func (d *ResourceData) SetId(v string) { 186 d.once.Do(d.init) 187 d.newState.ID = v 188 } 189 190 // SetConnInfo sets the connection info for a resource. 191 func (d *ResourceData) SetConnInfo(v map[string]string) { 192 d.once.Do(d.init) 193 d.newState.Ephemeral.ConnInfo = v 194 } 195 196 // State returns the new InstanceState after the diff and any Set 197 // calls. 198 func (d *ResourceData) State() *terraform.InstanceState { 199 var result terraform.InstanceState 200 result.ID = d.Id() 201 202 // If we have no ID, then this resource doesn't exist and we just 203 // return nil. 204 if result.ID == "" { 205 return nil 206 } 207 208 result.Attributes = d.stateObject("", d.schema) 209 result.Ephemeral.ConnInfo = d.ConnInfo() 210 211 if v := d.Id(); v != "" { 212 result.Attributes["id"] = d.Id() 213 } 214 215 return &result 216 } 217 218 func (d *ResourceData) init() { 219 var copyState terraform.InstanceState 220 if d.state != nil { 221 copyState = *d.state 222 } 223 224 d.newState = ©State 225 } 226 227 func (d *ResourceData) diffChange( 228 k string) (interface{}, interface{}, bool, bool) { 229 // Get the change between the state and the config. 230 o, n := d.getChange(k, getSourceState, getSourceConfig|getSourceExact) 231 if !o.Exists { 232 o.Value = nil 233 } 234 if !n.Exists { 235 n.Value = nil 236 } 237 238 // Return the old, new, and whether there is a change 239 return o.Value, n.Value, !reflect.DeepEqual(o.Value, n.Value), n.Computed 240 } 241 242 func (d *ResourceData) getChange( 243 key string, 244 oldLevel getSource, 245 newLevel getSource) (getResult, getResult) { 246 var parts, parts2 []string 247 if key != "" { 248 parts = strings.Split(key, ".") 249 parts2 = strings.Split(key, ".") 250 } 251 252 o := d.getObject("", parts, d.schema, oldLevel) 253 n := d.getObject("", parts2, d.schema, newLevel) 254 return o, n 255 } 256 257 func (d *ResourceData) get( 258 k string, 259 parts []string, 260 schema *Schema, 261 source getSource) getResult { 262 switch schema.Type { 263 case TypeList: 264 return d.getList(k, parts, schema, source) 265 case TypeMap: 266 return d.getMap(k, parts, schema, source) 267 case TypeSet: 268 return d.getSet(k, parts, schema, source) 269 case TypeBool: 270 fallthrough 271 case TypeInt: 272 fallthrough 273 case TypeString: 274 return d.getPrimitive(k, parts, schema, source) 275 default: 276 panic(fmt.Sprintf("%s: unknown type %#v", k, schema.Type)) 277 } 278 } 279 280 func (d *ResourceData) getSet( 281 k string, 282 parts []string, 283 schema *Schema, 284 source getSource) getResult { 285 s := &Set{F: schema.Set} 286 result := getResult{Schema: schema, Value: s} 287 288 // Get the list. For sets, the entire source must be exact: the 289 // entire set must come from set, diff, state, etc. So we go backwards 290 // and once we get a result, we take it. Or, we never get a result. 291 var raw getResult 292 for listSource := source; listSource > 0; listSource >>= 1 { 293 if source&getSourceExact != 0 && listSource != source { 294 break 295 } 296 297 raw = d.getList(k, nil, schema, listSource|getSourceExact) 298 if raw.Exists { 299 break 300 } 301 } 302 if !raw.Exists { 303 if len(parts) > 0 { 304 return d.getList(k, parts, schema, source) 305 } 306 307 return result 308 } 309 310 // If the entire list is computed, then the entire set is 311 // necessarilly computed. 312 if raw.Computed { 313 result.Computed = true 314 return result 315 } 316 317 list := raw.Value.([]interface{}) 318 if len(list) == 0 { 319 if len(parts) > 0 { 320 return d.getList(k, parts, schema, source) 321 } 322 323 result.Exists = raw.Exists 324 return result 325 } 326 327 // This is a reverse map of hash code => index in config used to 328 // resolve direct set item lookup for turning into state. Confused? 329 // Read on... 330 // 331 // To create the state (the state* functions), a Get call is done 332 // with a full key such as "ports.0". The index of a set ("0") doesn't 333 // make a lot of sense, but we need to deterministically list out 334 // elements of the set like this. Luckily, same sets have a deterministic 335 // List() output, so we can use that to look things up. 336 // 337 // This mapping makes it so that we can look up the hash code of an 338 // object back to its index in the REAL config. 339 var indexMap map[int]int 340 if len(parts) > 0 { 341 indexMap = make(map[int]int) 342 } 343 344 // Build the set from all the items using the given hash code 345 for i, v := range list { 346 code := s.add(v) 347 if indexMap != nil { 348 indexMap[code] = i 349 } 350 } 351 352 // If we're trying to get a specific element, then rewrite the 353 // index to be just that, then jump direct to getList. 354 if len(parts) > 0 { 355 index := parts[0] 356 indexInt, err := strconv.ParseInt(index, 0, 0) 357 if err != nil { 358 return getResultEmpty 359 } 360 361 codes := s.listCode() 362 if int(indexInt) >= len(codes) { 363 return getResultEmpty 364 } 365 code := codes[indexInt] 366 realIndex := indexMap[code] 367 368 parts[0] = strconv.FormatInt(int64(realIndex), 10) 369 return d.getList(k, parts, schema, source) 370 } 371 372 result.Exists = true 373 return result 374 } 375 376 func (d *ResourceData) getMap( 377 k string, 378 parts []string, 379 schema *Schema, 380 source getSource) getResult { 381 elemSchema := &Schema{Type: TypeString} 382 383 result := make(map[string]interface{}) 384 resultSet := false 385 prefix := k + "." 386 387 exact := source&getSourceExact != 0 388 source &^= getSourceExact 389 390 if !exact || source == getSourceState { 391 if d.state != nil && source >= getSourceState { 392 for k, _ := range d.state.Attributes { 393 if !strings.HasPrefix(k, prefix) { 394 continue 395 } 396 397 single := k[len(prefix):] 398 result[single] = d.getPrimitive(k, nil, elemSchema, source).Value 399 resultSet = true 400 } 401 } 402 } 403 404 if d.config != nil && source == getSourceConfig { 405 // For config, we always set the result to exactly what was requested 406 if mraw, ok := d.config.Get(k); ok { 407 result = make(map[string]interface{}) 408 switch m := mraw.(type) { 409 case []interface{}: 410 for _, innerRaw := range m { 411 for k, v := range innerRaw.(map[string]interface{}) { 412 result[k] = v 413 } 414 } 415 416 resultSet = true 417 case []map[string]interface{}: 418 for _, innerRaw := range m { 419 for k, v := range innerRaw { 420 result[k] = v 421 } 422 } 423 424 resultSet = true 425 case map[string]interface{}: 426 result = m 427 resultSet = true 428 default: 429 panic(fmt.Sprintf("unknown type: %#v", mraw)) 430 } 431 } else { 432 result = nil 433 } 434 } 435 436 if !exact || source == getSourceDiff { 437 if d.diff != nil && source >= getSourceDiff { 438 for k, v := range d.diff.Attributes { 439 if !strings.HasPrefix(k, prefix) { 440 continue 441 } 442 resultSet = true 443 444 single := k[len(prefix):] 445 446 if v.NewRemoved { 447 delete(result, single) 448 } else { 449 result[single] = d.getPrimitive(k, nil, elemSchema, source).Value 450 } 451 } 452 } 453 } 454 455 if !exact || source == getSourceSet { 456 if d.setMap != nil && source >= getSourceSet { 457 cleared := false 458 for k, _ := range d.setMap { 459 if !strings.HasPrefix(k, prefix) { 460 continue 461 } 462 resultSet = true 463 464 if !cleared { 465 // We clear the results if they are in the set map 466 result = make(map[string]interface{}) 467 cleared = true 468 } 469 470 single := k[len(prefix):] 471 result[single] = d.getPrimitive(k, nil, elemSchema, source).Value 472 } 473 } 474 } 475 476 // If we're requesting a specific element, return that 477 var resultValue interface{} = result 478 if len(parts) > 0 { 479 resultValue = result[parts[0]] 480 } 481 482 return getResult{ 483 Value: resultValue, 484 Exists: resultSet, 485 Schema: schema, 486 } 487 } 488 489 func (d *ResourceData) getObject( 490 k string, 491 parts []string, 492 schema map[string]*Schema, 493 source getSource) getResult { 494 if len(parts) > 0 { 495 // We're requesting a specific key in an object 496 key := parts[0] 497 parts = parts[1:] 498 s, ok := schema[key] 499 if !ok { 500 return getResultEmpty 501 } 502 503 if k != "" { 504 // If we're not at the root, then we need to append 505 // the key to get the full key path. 506 key = fmt.Sprintf("%s.%s", k, key) 507 } 508 509 return d.get(key, parts, s, source) 510 } 511 512 // Get the entire object 513 result := make(map[string]interface{}) 514 for field, _ := range schema { 515 result[field] = d.getObject(k, []string{field}, schema, source).Value 516 } 517 518 return getResult{ 519 Value: result, 520 Exists: true, 521 Schema: &Schema{ 522 Elem: schema, 523 }, 524 } 525 } 526 527 func (d *ResourceData) getList( 528 k string, 529 parts []string, 530 schema *Schema, 531 source getSource) getResult { 532 if len(parts) > 0 { 533 // We still have parts left over meaning we're accessing an 534 // element of this list. 535 idx := parts[0] 536 parts = parts[1:] 537 538 // Special case if we're accessing the count of the list 539 if idx == "#" { 540 schema := &Schema{Type: TypeInt} 541 return d.get(k+".#", parts, schema, source) 542 } 543 544 key := fmt.Sprintf("%s.%s", k, idx) 545 switch t := schema.Elem.(type) { 546 case *Resource: 547 return d.getObject(key, parts, t.Schema, source) 548 case *Schema: 549 return d.get(key, parts, t, source) 550 } 551 } 552 553 // Get the entire list. 554 var result []interface{} 555 count := d.getList(k, []string{"#"}, schema, source) 556 if !count.Computed { 557 result = make([]interface{}, count.Value.(int)) 558 for i, _ := range result { 559 is := strconv.FormatInt(int64(i), 10) 560 result[i] = d.getList(k, []string{is}, schema, source).Value 561 } 562 } 563 564 return getResult{ 565 Value: result, 566 Computed: count.Computed, 567 Exists: count.Exists, 568 Schema: schema, 569 } 570 } 571 572 func (d *ResourceData) getPrimitive( 573 k string, 574 parts []string, 575 schema *Schema, 576 source getSource) getResult { 577 var result string 578 var resultProcessed interface{} 579 var resultComputed, resultSet bool 580 exact := source&getSourceExact != 0 581 source &^= getSourceExact 582 583 if !exact || source == getSourceState { 584 if d.state != nil && source >= getSourceState { 585 result, resultSet = d.state.Attributes[k] 586 } 587 } 588 589 // No exact check is needed here because config is always exact 590 if d.config != nil && source == getSourceConfig { 591 // For config, we always return the exact value 592 if v, ok := d.config.Get(k); ok { 593 if err := mapstructure.WeakDecode(v, &result); err != nil { 594 panic(err) 595 } 596 597 resultSet = true 598 } else { 599 result = "" 600 resultSet = false 601 } 602 603 // If it is computed, set that. 604 resultComputed = d.config.IsComputed(k) 605 } 606 607 if !exact || source == getSourceDiff { 608 if d.diff != nil && source >= getSourceDiff { 609 attrD, ok := d.diff.Attributes[k] 610 if ok { 611 if !attrD.NewComputed { 612 result = attrD.New 613 if attrD.NewExtra != nil { 614 // If NewExtra != nil, then we have processed data as the New, 615 // so we store that but decode the unprocessed data into result 616 resultProcessed = result 617 618 err := mapstructure.WeakDecode(attrD.NewExtra, &result) 619 if err != nil { 620 panic(err) 621 } 622 } 623 624 resultSet = true 625 } else { 626 result = "" 627 resultSet = false 628 } 629 } 630 } 631 } 632 633 if !exact || source == getSourceSet { 634 if d.setMap != nil && source >= getSourceSet { 635 if v, ok := d.setMap[k]; ok { 636 result = v 637 resultSet = true 638 } 639 } 640 } 641 642 if !resultSet { 643 result = "" 644 } 645 646 var resultValue interface{} 647 switch schema.Type { 648 case TypeBool: 649 if result == "" { 650 resultValue = false 651 break 652 } 653 654 v, err := strconv.ParseBool(result) 655 if err != nil { 656 panic(err) 657 } 658 659 resultValue = v 660 case TypeString: 661 // Use the value as-is. We just put this case here to be explicit. 662 resultValue = result 663 case TypeInt: 664 if result == "" { 665 resultValue = 0 666 break 667 } 668 669 if resultComputed { 670 break 671 } 672 673 v, err := strconv.ParseInt(result, 0, 0) 674 if err != nil { 675 panic(err) 676 } 677 678 resultValue = int(v) 679 default: 680 panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) 681 } 682 683 return getResult{ 684 Value: resultValue, 685 ValueProcessed: resultProcessed, 686 Computed: resultComputed, 687 Exists: resultSet, 688 Schema: schema, 689 } 690 } 691 692 func (d *ResourceData) set( 693 k string, 694 parts []string, 695 schema *Schema, 696 value interface{}) error { 697 switch schema.Type { 698 case TypeList: 699 return d.setList(k, parts, schema, value) 700 case TypeMap: 701 return d.setMapValue(k, parts, schema, value) 702 case TypeSet: 703 return d.setSet(k, parts, schema, value) 704 case TypeBool: 705 fallthrough 706 case TypeInt: 707 fallthrough 708 case TypeString: 709 return d.setPrimitive(k, schema, value) 710 default: 711 panic(fmt.Sprintf("%s: unknown type %#v", k, schema.Type)) 712 } 713 } 714 715 func (d *ResourceData) setList( 716 k string, 717 parts []string, 718 schema *Schema, 719 value interface{}) error { 720 if len(parts) > 0 { 721 // We're setting a specific element 722 idx := parts[0] 723 parts = parts[1:] 724 725 // Special case if we're accessing the count of the list 726 if idx == "#" { 727 return fmt.Errorf("%s: can't set count of list", k) 728 } 729 730 key := fmt.Sprintf("%s.%s", k, idx) 731 switch t := schema.Elem.(type) { 732 case *Resource: 733 return d.setObject(key, parts, t.Schema, value) 734 case *Schema: 735 return d.set(key, parts, t, value) 736 } 737 } 738 739 var vs []interface{} 740 if err := mapstructure.Decode(value, &vs); err != nil { 741 return fmt.Errorf("%s: %s", k, err) 742 } 743 744 // Set the entire list. 745 var err error 746 for i, elem := range vs { 747 is := strconv.FormatInt(int64(i), 10) 748 err = d.setList(k, []string{is}, schema, elem) 749 if err != nil { 750 break 751 } 752 } 753 if err != nil { 754 for i, _ := range vs { 755 is := strconv.FormatInt(int64(i), 10) 756 d.setList(k, []string{is}, schema, nil) 757 } 758 759 return err 760 } 761 762 d.setMap[k+".#"] = strconv.FormatInt(int64(len(vs)), 10) 763 return nil 764 } 765 766 func (d *ResourceData) setMapValue( 767 k string, 768 parts []string, 769 schema *Schema, 770 value interface{}) error { 771 elemSchema := &Schema{Type: TypeString} 772 if len(parts) > 0 { 773 return fmt.Errorf("%s: full map must be set, no a single element", k) 774 } 775 776 // Delete any prior map set 777 /* 778 v := d.getMap(k, nil, schema, getSourceSet) 779 for subKey, _ := range v.(map[string]interface{}) { 780 delete(d.setMap, fmt.Sprintf("%s.%s", k, subKey)) 781 } 782 */ 783 784 v := reflect.ValueOf(value) 785 if v.Kind() != reflect.Map { 786 return fmt.Errorf("%s: must be a map", k) 787 } 788 if v.Type().Key().Kind() != reflect.String { 789 return fmt.Errorf("%s: keys must strings", k) 790 } 791 vs := make(map[string]interface{}) 792 for _, mk := range v.MapKeys() { 793 mv := v.MapIndex(mk) 794 vs[mk.String()] = mv.Interface() 795 } 796 797 for subKey, v := range vs { 798 err := d.set(fmt.Sprintf("%s.%s", k, subKey), nil, elemSchema, v) 799 if err != nil { 800 return err 801 } 802 } 803 804 return nil 805 } 806 807 func (d *ResourceData) setObject( 808 k string, 809 parts []string, 810 schema map[string]*Schema, 811 value interface{}) error { 812 if len(parts) > 0 { 813 // We're setting a specific key in an object 814 key := parts[0] 815 parts = parts[1:] 816 817 s, ok := schema[key] 818 if !ok { 819 return fmt.Errorf("%s (internal): unknown key to set: %s", k, key) 820 } 821 822 if k != "" { 823 // If we're not at the root, then we need to append 824 // the key to get the full key path. 825 key = fmt.Sprintf("%s.%s", k, key) 826 } 827 828 return d.set(key, parts, s, value) 829 } 830 831 // Set the entire object. First decode into a proper structure 832 var v map[string]interface{} 833 if err := mapstructure.Decode(value, &v); err != nil { 834 return fmt.Errorf("%s: %s", k, err) 835 } 836 837 // Set each element in turn 838 var err error 839 for k1, v1 := range v { 840 err = d.setObject(k, []string{k1}, schema, v1) 841 if err != nil { 842 break 843 } 844 } 845 if err != nil { 846 for k1, _ := range v { 847 d.setObject(k, []string{k1}, schema, nil) 848 } 849 } 850 851 return err 852 } 853 854 func (d *ResourceData) setPrimitive( 855 k string, 856 schema *Schema, 857 v interface{}) error { 858 if v == nil { 859 delete(d.setMap, k) 860 return nil 861 } 862 863 var set string 864 switch schema.Type { 865 case TypeBool: 866 var b bool 867 if err := mapstructure.Decode(v, &b); err != nil { 868 return fmt.Errorf("%s: %s", k, err) 869 } 870 871 set = strconv.FormatBool(b) 872 case TypeString: 873 if err := mapstructure.Decode(v, &set); err != nil { 874 return fmt.Errorf("%s: %s", k, err) 875 } 876 case TypeInt: 877 var n int 878 if err := mapstructure.Decode(v, &n); err != nil { 879 return fmt.Errorf("%s: %s", k, err) 880 } 881 882 set = strconv.FormatInt(int64(n), 10) 883 default: 884 return fmt.Errorf("Unknown type: %#v", schema.Type) 885 } 886 887 d.setMap[k] = set 888 return nil 889 } 890 891 func (d *ResourceData) setSet( 892 k string, 893 parts []string, 894 schema *Schema, 895 value interface{}) error { 896 if len(parts) > 0 { 897 return fmt.Errorf("%s: can only set the full set, not elements", k) 898 } 899 900 // If it is a slice, then we have to turn it into a *Set so that 901 // we get the proper order back based on the hash code. 902 if v := reflect.ValueOf(value); v.Kind() == reflect.Slice { 903 // Set the entire list, this lets us get sane values out of it 904 if err := d.setList(k, nil, schema, value); err != nil { 905 return err 906 } 907 908 // Build the set by going over the list items in order and 909 // hashing them into the set. The reason we go over the list and 910 // not the `value` directly is because this forces all types 911 // to become []interface{} (generic) instead of []string, which 912 // most hash functions are expecting. 913 s := &Set{F: schema.Set} 914 source := getSourceSet | getSourceExact 915 for i := 0; i < v.Len(); i++ { 916 is := strconv.FormatInt(int64(i), 10) 917 result := d.getList(k, []string{is}, schema, source) 918 if !result.Exists { 919 panic("just set item doesn't exist") 920 } 921 922 s.Add(result.Value) 923 } 924 925 value = s 926 } 927 928 if s, ok := value.(*Set); ok { 929 value = s.List() 930 } 931 932 return d.setList(k, nil, schema, value) 933 } 934 935 func (d *ResourceData) stateList( 936 prefix string, 937 schema *Schema) map[string]string { 938 countRaw := d.get(prefix, []string{"#"}, schema, d.stateSource(prefix)) 939 if !countRaw.Exists { 940 if schema.Computed { 941 // If it is computed, then it always _exists_ in the state, 942 // it is just empty. 943 countRaw.Exists = true 944 countRaw.Value = 0 945 } else { 946 return nil 947 } 948 } 949 count := countRaw.Value.(int) 950 951 result := make(map[string]string) 952 if count > 0 || schema.Computed { 953 result[prefix+".#"] = strconv.FormatInt(int64(count), 10) 954 } 955 for i := 0; i < count; i++ { 956 key := fmt.Sprintf("%s.%d", prefix, i) 957 958 var m map[string]string 959 switch t := schema.Elem.(type) { 960 case *Resource: 961 m = d.stateObject(key, t.Schema) 962 case *Schema: 963 m = d.stateSingle(key, t) 964 } 965 966 for k, v := range m { 967 result[k] = v 968 } 969 } 970 971 return result 972 } 973 974 func (d *ResourceData) stateMap( 975 prefix string, 976 schema *Schema) map[string]string { 977 v := d.getMap(prefix, nil, schema, d.stateSource(prefix)) 978 if !v.Exists { 979 return nil 980 } 981 982 elemSchema := &Schema{Type: TypeString} 983 result := make(map[string]string) 984 for mk, _ := range v.Value.(map[string]interface{}) { 985 mp := fmt.Sprintf("%s.%s", prefix, mk) 986 for k, v := range d.stateSingle(mp, elemSchema) { 987 result[k] = v 988 } 989 } 990 991 return result 992 } 993 994 func (d *ResourceData) stateObject( 995 prefix string, 996 schema map[string]*Schema) map[string]string { 997 result := make(map[string]string) 998 for k, v := range schema { 999 key := k 1000 if prefix != "" { 1001 key = prefix + "." + key 1002 } 1003 1004 for k1, v1 := range d.stateSingle(key, v) { 1005 result[k1] = v1 1006 } 1007 } 1008 1009 return result 1010 } 1011 1012 func (d *ResourceData) statePrimitive( 1013 prefix string, 1014 schema *Schema) map[string]string { 1015 raw := d.getRaw(prefix, d.stateSource(prefix)) 1016 if !raw.Exists { 1017 return nil 1018 } 1019 1020 v := raw.Value 1021 if raw.ValueProcessed != nil { 1022 v = raw.ValueProcessed 1023 } 1024 1025 var vs string 1026 switch schema.Type { 1027 case TypeBool: 1028 vs = strconv.FormatBool(v.(bool)) 1029 case TypeString: 1030 vs = v.(string) 1031 case TypeInt: 1032 vs = strconv.FormatInt(int64(v.(int)), 10) 1033 default: 1034 panic(fmt.Sprintf("Unknown type: %#v", schema.Type)) 1035 } 1036 1037 return map[string]string{ 1038 prefix: vs, 1039 } 1040 } 1041 1042 func (d *ResourceData) stateSet( 1043 prefix string, 1044 schema *Schema) map[string]string { 1045 raw := d.get(prefix, nil, schema, d.stateSource(prefix)) 1046 if !raw.Exists { 1047 if schema.Computed { 1048 // If it is computed, then it always _exists_ in the state, 1049 // it is just empty. 1050 raw.Exists = true 1051 raw.Value = new(Set) 1052 } else { 1053 return nil 1054 } 1055 } 1056 1057 set := raw.Value.(*Set) 1058 list := set.List() 1059 result := make(map[string]string) 1060 result[prefix+".#"] = strconv.FormatInt(int64(len(list)), 10) 1061 for i := 0; i < len(list); i++ { 1062 key := fmt.Sprintf("%s.%d", prefix, i) 1063 1064 var m map[string]string 1065 switch t := schema.Elem.(type) { 1066 case *Resource: 1067 m = d.stateObject(key, t.Schema) 1068 case *Schema: 1069 m = d.stateSingle(key, t) 1070 } 1071 1072 for k, v := range m { 1073 result[k] = v 1074 } 1075 } 1076 1077 return result 1078 } 1079 1080 func (d *ResourceData) stateSingle( 1081 prefix string, 1082 schema *Schema) map[string]string { 1083 switch schema.Type { 1084 case TypeList: 1085 return d.stateList(prefix, schema) 1086 case TypeMap: 1087 return d.stateMap(prefix, schema) 1088 case TypeSet: 1089 return d.stateSet(prefix, schema) 1090 case TypeBool: 1091 fallthrough 1092 case TypeInt: 1093 fallthrough 1094 case TypeString: 1095 return d.statePrimitive(prefix, schema) 1096 default: 1097 panic(fmt.Sprintf("%s: unknown type %#v", prefix, schema.Type)) 1098 } 1099 } 1100 1101 func (d *ResourceData) stateSource(prefix string) getSource { 1102 // If we're not doing a partial apply, then get the set level 1103 if !d.partial { 1104 return getSourceSet 1105 } 1106 1107 // Otherwise, only return getSourceSet if its in the partial map. 1108 // Otherwise we use state level only. 1109 for k, _ := range d.partialMap { 1110 if strings.HasPrefix(prefix, k) { 1111 return getSourceSet 1112 } 1113 } 1114 1115 return getSourceState 1116 }