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 = &copyState
   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  }