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