github.com/chalford/terraform@v0.3.7-0.20150113080010-a78c69a8c81f/helper/schema/field_reader_config.go (about)

     1  package schema
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  	"sync"
     8  
     9  	"github.com/hashicorp/terraform/terraform"
    10  	"github.com/mitchellh/mapstructure"
    11  )
    12  
    13  // ConfigFieldReader reads fields out of an untyped map[string]string to
    14  // the best of its ability.
    15  type ConfigFieldReader struct {
    16  	Config *terraform.ResourceConfig
    17  	Schema map[string]*Schema
    18  
    19  	lock sync.Mutex
    20  }
    21  
    22  func (r *ConfigFieldReader) ReadField(address []string) (FieldReadResult, error) {
    23  	return r.readField(address, false)
    24  }
    25  
    26  func (r *ConfigFieldReader) readField(
    27  	address []string, nested bool) (FieldReadResult, error) {
    28  	schemaList := addrToSchema(address, r.Schema)
    29  	if len(schemaList) == 0 {
    30  		return FieldReadResult{}, nil
    31  	}
    32  
    33  	if !nested {
    34  		// If we have a set anywhere in the address, then we need to
    35  		// read that set out in order and actually replace that part of
    36  		// the address with the real list index. i.e. set.50 might actually
    37  		// map to set.12 in the config, since it is in list order in the
    38  		// config, not indexed by set value.
    39  		for i, v := range schemaList {
    40  			// Sets are the only thing that cause this issue.
    41  			if v.Type != TypeSet {
    42  				continue
    43  			}
    44  
    45  			// If we're at the end of the list, then we don't have to worry
    46  			// about this because we're just requesting the whole set.
    47  			if i == len(schemaList)-1 {
    48  				continue
    49  			}
    50  
    51  			// If we're looking for the count, then ignore...
    52  			if address[i+1] == "#" {
    53  				continue
    54  			}
    55  
    56  			// Get the code
    57  			code, err := strconv.ParseInt(address[i+1], 0, 0)
    58  			if err != nil {
    59  				return FieldReadResult{}, err
    60  			}
    61  
    62  			// Get the set so we can get the index map that tells us the
    63  			// mapping of the hash code to the list index
    64  			_, indexMap, err := r.readSet(address[:i+1], v)
    65  			if err != nil {
    66  				return FieldReadResult{}, err
    67  			}
    68  
    69  			index, ok := indexMap[int(code)]
    70  			if !ok {
    71  				return FieldReadResult{}, nil
    72  			}
    73  
    74  			address[i+1] = strconv.FormatInt(int64(index), 10)
    75  		}
    76  	}
    77  
    78  	k := strings.Join(address, ".")
    79  	schema := schemaList[len(schemaList)-1]
    80  	switch schema.Type {
    81  	case TypeBool:
    82  		fallthrough
    83  	case TypeFloat:
    84  		fallthrough
    85  	case TypeInt:
    86  		fallthrough
    87  	case TypeString:
    88  		return r.readPrimitive(k, schema)
    89  	case TypeList:
    90  		return readListField(&nestedConfigFieldReader{r}, address, schema)
    91  	case TypeMap:
    92  		return r.readMap(k)
    93  	case TypeSet:
    94  		result, _, err := r.readSet(address, schema)
    95  		return result, err
    96  	case typeObject:
    97  		return readObjectField(
    98  			&nestedConfigFieldReader{r},
    99  			address, schema.Elem.(map[string]*Schema))
   100  	default:
   101  		panic(fmt.Sprintf("Unknown type: %s", schema.Type))
   102  	}
   103  }
   104  
   105  func (r *ConfigFieldReader) readMap(k string) (FieldReadResult, error) {
   106  	mraw, ok := r.Config.Get(k)
   107  	if !ok {
   108  		return FieldReadResult{}, nil
   109  	}
   110  
   111  	result := make(map[string]interface{})
   112  	switch m := mraw.(type) {
   113  	case []interface{}:
   114  		for _, innerRaw := range m {
   115  			for k, v := range innerRaw.(map[string]interface{}) {
   116  				result[k] = v
   117  			}
   118  		}
   119  	case []map[string]interface{}:
   120  		for _, innerRaw := range m {
   121  			for k, v := range innerRaw {
   122  				result[k] = v
   123  			}
   124  		}
   125  	case map[string]interface{}:
   126  		result = m
   127  	default:
   128  		panic(fmt.Sprintf("unknown type: %#v", mraw))
   129  	}
   130  
   131  	return FieldReadResult{
   132  		Value:  result,
   133  		Exists: true,
   134  	}, nil
   135  }
   136  
   137  func (r *ConfigFieldReader) readPrimitive(
   138  	k string, schema *Schema) (FieldReadResult, error) {
   139  	raw, ok := r.Config.Get(k)
   140  	if !ok {
   141  		return FieldReadResult{}, nil
   142  	}
   143  
   144  	var result string
   145  	if err := mapstructure.WeakDecode(raw, &result); err != nil {
   146  		return FieldReadResult{}, err
   147  	}
   148  
   149  	computed := r.Config.IsComputed(k)
   150  	returnVal, err := stringToPrimitive(result, computed, schema)
   151  	if err != nil {
   152  		return FieldReadResult{}, err
   153  	}
   154  
   155  	return FieldReadResult{
   156  		Value:    returnVal,
   157  		Exists:   true,
   158  		Computed: computed,
   159  	}, nil
   160  }
   161  
   162  func (r *ConfigFieldReader) readSet(
   163  	address []string, schema *Schema) (FieldReadResult, map[int]int, error) {
   164  	indexMap := make(map[int]int)
   165  	// Create the set that will be our result
   166  	set := &Set{F: schema.Set}
   167  
   168  	raw, err := readListField(&nestedConfigFieldReader{r}, address, schema)
   169  	if err != nil {
   170  		return FieldReadResult{}, indexMap, err
   171  	}
   172  	if !raw.Exists {
   173  		return FieldReadResult{Value: set}, indexMap, nil
   174  	}
   175  
   176  	// If the list is computed, the set is necessarilly computed
   177  	if raw.Computed {
   178  		return FieldReadResult{
   179  			Value:    set,
   180  			Exists:   true,
   181  			Computed: raw.Computed,
   182  		}, indexMap, nil
   183  	}
   184  
   185  	// Build up the set from the list elements
   186  	for i, v := range raw.Value.([]interface{}) {
   187  		// Check if any of the keys in this item are computed
   188  		computed := r.hasComputedSubKeys(
   189  			fmt.Sprintf("%s.%d", strings.Join(address, "."), i), schema)
   190  
   191  		code := set.add(v)
   192  		indexMap[code] = i
   193  		if computed {
   194  			set.m[-code] = set.m[code]
   195  			delete(set.m, code)
   196  			code = -code
   197  		}
   198  	}
   199  
   200  	return FieldReadResult{
   201  		Value:  set,
   202  		Exists: true,
   203  	}, indexMap, nil
   204  }
   205  
   206  // hasComputedSubKeys walks through a schema and returns whether or not the
   207  // given key contains any subkeys that are computed.
   208  func (r *ConfigFieldReader) hasComputedSubKeys(key string, schema *Schema) bool {
   209  	prefix := key + "."
   210  
   211  	switch t := schema.Elem.(type) {
   212  	case *Resource:
   213  		for k, schema := range t.Schema {
   214  			if r.Config.IsComputed(prefix + k) {
   215  				return true
   216  			}
   217  
   218  			if r.hasComputedSubKeys(prefix+k, schema) {
   219  				return true
   220  			}
   221  		}
   222  	}
   223  
   224  	return false
   225  }
   226  
   227  // nestedConfigFieldReader is a funny little thing that just wraps a
   228  // ConfigFieldReader to call readField when ReadField is called so that
   229  // we don't recalculate the set rewrites in the address, which leads to
   230  // an infinite loop.
   231  type nestedConfigFieldReader struct {
   232  	Reader *ConfigFieldReader
   233  }
   234  
   235  func (r *nestedConfigFieldReader) ReadField(
   236  	address []string) (FieldReadResult, error) {
   237  	return r.Reader.readField(address, true)
   238  }