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