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